Estratégia de negociação simples: Reversão à média
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.
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).
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.
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
- 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