Aplicação da transformada de Fisher e da transformada inversa de Fisher à análise de mercado no MetaTrader 5

investeo | 6 março, 2014

Introdução

Este artigo apresenta a aplicação da transformada de Fisher e da transformada inversa de Fisher aos mercados financeiros.

A teoria de transformada de Fisher é colocada em prática pela implementação da versão MQL5 do indicador RSI suavizado de transformada inversa de Fisher, apresentada na edição de outubro de 2010 da revista "Stocks and Commodities". A lucratividade do indicador é testada retroativamente pelo Expert Advisor que utiliza sinais baseados no indicador de Fisher.

O artigo é baseado nos livros de J.F.Ehlers e em artigos encontrados na Internet. Todas as referências são citadas ao final do artigo.


1. Função densidade de probabilidade (PDF) de Gauss vs. ciclos de mercado

Uma suposição comum é que os preços têm função densidade de probabilidade normal.

Isto significa que os desvios de preço em relação à média podem ser descritos como uma curva de Gauss bem conhecida:

Figura 1. Distribuição de Gauss

Figura 1. Curva de Gauss

Eu falei sobre função densidade de probabilidade normal. Para entender totalmente o que isso significa, vamos introduzir diversas ideias e fórmulas matemáticas. Esperamos que tudo isso seja compreensível para a maioria dos leitores.

No dicionário Merriam-Webster, probabilidade é definida como:

  1. A razão entre o número de resultados em um conjunto exaustivo de resultados igualmente prováveis que produzem um determinado evento e o número total de resultados possíveis ou
  2. A chance de um determinado evento ocorrer.

Uma variável aleatória é uma variável cujo valor resulta de uma medição em algum tipo de processo aleatório. Em nosso caso, a variável aleatória é o preço de um ativo.

Finalmente, PDF é a sigla de Função densidade de probabilidade - uma função que descreve a probabilidade de uma variável aleatória X (outra vez, preço, em nosso caso) assumir um valor dentro de uma determinada gama de valores possíveis. Um valor de variável aleatória resultante de uma distribuição de Gauss ou distribuição normal é uma distribuição de probabilidade que é frequentemente utilizada para descrever variáveis aleatórias do mundo real que tendem a se agrupar ao redor de um valor médio único.

Matematicamente falando, a probabilidade de a variável aleatória X assumir um valor que esteja no intervalo [a,b] é definida como integral.

Figura 2. Integral da densidade de probabilidade

Isto representa a área sob a curva f(x), de "a" a "b". A probabilidade é contada de 0 a 100% ou de 0 a 1,00, de forma que há um limite que estabelece que a área total sob a curva f(x) deve ser igual a 1 (soma das probabilidades):

Figura 3. área total sob a curva

Agora, vamos voltar à parte inferior da Figura 1:

Figura 4. Parte inferior da figura de Gauss

Figura 2. Desvios padrão da curva de Gauss

Aqui você pode ver qual é a porcentagem de valores que está a uma distância de +/- 1-3 de desvio padrão (sigmas) em relação à média. Com a PDF de Gauss, 68,27% das ocorrências estão dentro de +/- 1 desvio padrão em relação à média, 95,45% estão dentro de +/- 2 desvios padrão, e 99,73% estão dentro de +/- 3 desvios padrão.

Você pensa que o mesmo acontece com dados reais do mercado? Não é bem assim. Quando observamos os preços de mercado, podemos supor que o gráfico parece uma onda quadrada - após quebrar os níveis de resistência ou eis de suporte em que ordens de alto valor são agrupadas, os preços tendem a subir ou cair para o próximo nível de suporte/resistência. é esta a razão pela qual o mercado pode ser modelado com grande semelhança a uma onda quadrada ou senoidal.

Por favor, observe o gráfico da onda senoidal abaixo:

onda senoidal

Figura 3. Gráfico da onda senoidal

Você deve notar que, na realidade, a maioria das negociações são similarmente posicionadas perto dos níveis de suporte e resistência, o que parece bem natural. Agora eu representarei o gráfico de densidade de uma onda senoidal. Você pode imaginar que estamos girando a Figura 3 em 90 graus para a direito e deixar todos os círculos do gráfico caírem:

Densidade

Figura 4. Gráfico de densidade da onda senoidal

Você pode notar que a densidade é maior nas posições das extremidades esquerda e direita. Isto parece compatível com a afirmação anterior de que a maioria das negociações são feitas muito próximas dos níveis de resistência e suporte. Vamos verificar quais são as porcentagens de ocorrências, traçando um histograma:

Histograma

Figura 5. Histograma de densidade da onda senoidal

Isso parece uma curva de Gauss? Não exatamente. A primeira e a terceira barra parecem ter mais ocorrências.

J.F. Ehlers, em seu livro "Сybernetic analysis for stocks and futures", descreveu um experimento em que ele analisou títulos do Tesouro norte-americano durante um período de 15 anos. Ele aplicou um canal normalizado de 10 barras e mediu a posição do preço dentro de 100 binários e contou o número de vezes que o preço estava em cada binário. Os resultados desta distribuição de probabilidade lembram os de uma onda senoidal.


2. Transformada de Fisher e sua aplicação em séries temporais

Agora que sabemos que a PDF de um ciclo de mercado não se parece com uma curva de Gauss e sim com uma PDF de onda senoidal e que a maioria dos indicadores supõe que a PDF de ciclo de mercado seja uma curva de Gauss, precisamos encontrar uma maneira de "corrigir" isso. A solução é utilizar a transformada de Fisher. A transformada de Fisher faz com que a PDF de qualquer forma de onde se aproxime a uma onda de Gauss.

A equação para a transformada de Fisher é:

Figura 6. Equação da transformada de Fisher,

Figura 5. Transformada de Fisher

Figura 6. Transformada de Fisher

Eu mencionei que a saída da transformada de Fisher é aproximadamente a PDF de Gauss. Para explicar isto, vale a pena observar a Figura 6.

Quando os dados de entrada estão perto da sua média, o ganho é aproximadamente um (veja o gráfico para |X<0,5|). Por outro lado, quando a entrada normalizada se aproxima de qualquer um dos limites, a saída é amplificada consideravelmente (vejo o gráfico para 0,5<|x|<1). Na prática, você poderá pensar no crescimento de uma cauda "praticamente gaussiana", quando a maioria dos desvios ocorre - isto é exatamente o que acontece à PDF transformada.

Como aplicar a transformada de Fisher à negociação? Primeiramente, devido à restrição |x|<1, os preços devem ser normalizados dentro deste período. Quando os preços normalizados estão sujeitos à transformada de Fisher, os movimentos extremos de preço tornam-se relativamente raros. Isso significa que a transformada de Fisher captura os movimentos extremos de preço e permite negociarmos de acordo com esses extremos.


3. Transformada de Fisher em MQL5

O código-fonte do indicador de transformada de Fisher está descrito no livro "Cybernetic Analysis for Stocks and Futures", de Ehler.

Ele já foi implementado em MQL4 e eu o converti para MQL5. O indicador utiliza preços medianos (H+L)/2. Eu utilizei a função iMA() para extrair os preços medianos do histórico.

Primeiramente, os preços são normalizados dentro do intervalo de 10 barras e os preços normalizados são submetidos à transformada de Fisher.

//+------------------------------------------------------------------+
//|                                              FisherTransform.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "MQL5 version of Fisher Transform indicator"

#property indicator_buffers 4
#property indicator_level1 0
#property indicator_levelcolor Silver
#property indicator_plots 2
#property indicator_type1         DRAW_LINE
#property indicator_color1        Red
#property indicator_width1 1
#property indicator_type2         DRAW_LINE
#property indicator_color2        Blue
#property indicator_width2 1

double Value1[];
double Fisher[];
double Trigger[];

input int Len=10;

double medianbuff[];
int hMedian;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,Fisher,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);
   SetIndexBuffer(2,Value1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,medianbuff,INDICATOR_CALCULATIONS);
   ArraySetAsSeries(Fisher,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Value1,true);
   ArraySetAsSeries(medianbuff,true);
   
   hMedian = iMA(_Symbol,PERIOD_CURRENT,1,0,MODE_SMA,PRICE_MEDIAN);
   if(hMedian==INVALID_HANDLE)
     {
      //--- tell about the failure and output the error code
      PrintFormat("Failed to create handle of the iMA indicator for the symbol %s/%s, error code %d",
                 _Symbol,
                 EnumToString(PERIOD_CURRENT),
                 GetLastError());
      //--- the indicator is stopped early, if the returned value is negative
      return(-1);
     }
//---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int  nLimit=MathMin(rates_total-Len-1,rates_total-prev_calculated);
   int copied = CopyBuffer(hMedian,0,0,nLimit,medianbuff);
   if (copied!=nLimit) return (-1);
   nLimit--;
   for(int i=nLimit; i>=0; i--) 
     {
      double price=medianbuff[i];
      double MaxH = price;
      double MinL = price;
      for(int j=0; j<Len; j++) 
        {
         double nprice=medianbuff[i+j];
         if (nprice > MaxH) MaxH = nprice;
         if (nprice < MinL) MinL = nprice;
        }
      Value1[i]=0.5*2.0 *((price-MinL)/(MaxH-MinL)-0.5)+0.5*Value1[i+1];
      if(Value1[i]>0.9999) Value1[i]=0.9999;
      if(Value1[i]<-0.9999) Value1[i]=-0.9999;
      Fisher[i]=0.25*MathLog((1+Value1[i])/(1-Value1[i]))+0.5*Fisher[i+1];
      Trigger[i]=Fisher[i+1];
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
    

Por favor, perceba que são gerados sinais agudos.

A linha de sinal é simplesmente o preço transformado de Fisher atrasado em uma barra:

Indicador de transformada de Fisher

Figura 7. Indicador de transformada de Fisher

4. Transformada inversa de Fisher e sua aplicação em indicadores de ciclo

A equação da transformada inversa de Fisher é obtida através da solução da equação da transformada de Fisher para x em termos de y.

Figura 8. Equação da transformada inversa de Fisher,

Figura 6. Transformada inversa de Fisher

Figura 8. Transformada inversa de Fisher

A resposta de transferência desta função é inversa à da transformada de Fisher.

Para |x|>2, a entrada é comprimida para não exceder um (para números negativos, -1, e para positivos, +1) e para |x|<1 ela é quase uma relação linear, o que significa que a saída tem mais ou menos as mesmas características da entrada.

Este é o resultado quando a transformada de Fisher é aplicada a dados de entrada adequadamente preparados. Há uma grande probabilidade de a saída ser -1 ou +1. Isto faz com que a transformada inversa de Fisher seja perfeita para aplicá-la aos indicadores do oscilador. A transformada inversa de Fisher pode melhorá-los ao fornecer sinais agudos de compra ou venda.

5. Exemplo de transformada inversa de Fisher em MQL5

Para verificação da transformada inversa de Fisher que implementei na versão MQL5 do indicador RSI suavizado de transformada inversa de Fisher de Sylvain Vervoort, apresentada na edição de outubro de 2010 da revista "Stocks and Commodities" e criação de um módulo de sinais de negociação e Expert Advisor baseados neste indicador.

O indicador de transformada inversa de Fisher já foi implementado em diversas plataformas de negociação. Os códigos fonte estão disponíveis em traders.com e na Base de códigos MQL5.com.

Porque não havia nenhuma função iRSIOnArray no MQL5, eu a adicionei ao código do indicador. A única diferença em relação ao indicador original é o RSIPeriod padrão definido em 21 e o EMAPeriod definido em 34, visto que isso ocasionou o melhor comportamento para as minhas configurações (EURUSD 1H). Você poderá querer alterar para RSIPeriod padrão 4 e EMAPeriod 4.

//+------------------------------------------------------------------+
//|                            SmoothedRSIInverseFisherTransform.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"
#property indicator_separate_window
#include <MovingAverages.mqh>
#property description "MQL5 version of Silvain Vervoort's Inverse RSI"
#property indicator_minimum -10
#property indicator_maximum 110
#property indicator_buffers 16
#property indicator_level1 12
#property indicator_level2 88
#property indicator_levelcolor Silver
#property indicator_plots 1
#property indicator_type1         DRAW_LINE
#property indicator_color1        LightSeaGreen
#property indicator_width1 2

int                  ma_period=10;             // period of ma
int                  ma_shift=0;               // shift
ENUM_MA_METHOD       ma_method=MODE_LWMA;        // type of smoothing
ENUM_APPLIED_PRICE   applied_price=PRICE_CLOSE// type of price

double wma0[];
double wma1[];
double wma2[];
double wma3[];
double wma4[];
double wma5[];
double wma6[];
double wma7[];
double wma8[];
double wma9[];
double ema0[];
double ema1[];
double rainbow[];
double rsi[];
double bufneg[];
double bufpos[];
double srsi[];
double fish[];

int hwma0;

int wma1weightsum;
int wma2weightsum;
int wma3weightsum;
int wma4weightsum;
int wma5weightsum;
int wma6weightsum;
int wma7weightsum;
int wma8weightsum;
int wma9weightsum;

extern int     RSIPeriod=21;
extern int     EMAPeriod=34;

  
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0,fish,INDICATOR_DATA);
   SetIndexBuffer(1,wma0,INDICATOR_CALCULATIONS);
   SetIndexBuffer(2,wma1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,wma2,INDICATOR_CALCULATIONS);
   SetIndexBuffer(4,wma3,INDICATOR_CALCULATIONS);
   SetIndexBuffer(5,wma4,INDICATOR_CALCULATIONS);
   SetIndexBuffer(6,wma5,INDICATOR_CALCULATIONS);
   SetIndexBuffer(7,wma6,INDICATOR_CALCULATIONS);
   SetIndexBuffer(8,wma7,INDICATOR_CALCULATIONS);
   SetIndexBuffer(9,wma8,INDICATOR_CALCULATIONS);
   SetIndexBuffer(10,wma9,INDICATOR_CALCULATIONS);
   SetIndexBuffer(11,rsi,INDICATOR_CALCULATIONS);
   SetIndexBuffer(12,ema0,INDICATOR_CALCULATIONS);
   SetIndexBuffer(13,srsi,INDICATOR_CALCULATIONS);
   SetIndexBuffer(14,ema1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(15,rainbow,INDICATOR_CALCULATIONS);

   ArraySetAsSeries(fish,true);
   ArraySetAsSeries(wma0,true);
   ArraySetAsSeries(wma1,true);
   ArraySetAsSeries(wma2,true);
   ArraySetAsSeries(wma3,true);
   ArraySetAsSeries(wma4,true);
   ArraySetAsSeries(wma5,true);
   ArraySetAsSeries(wma6,true);
   ArraySetAsSeries(wma7,true);
   ArraySetAsSeries(wma8,true);
   ArraySetAsSeries(wma9,true);
   ArraySetAsSeries(ema0,true);
   ArraySetAsSeries(ema1,true);
   ArraySetAsSeries(rsi,true);
   ArraySetAsSeries(srsi,true);
   ArraySetAsSeries(rainbow,true);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0);
//--- sets drawing line empty value
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
//--- digits
   IndicatorSetInteger(INDICATOR_DIGITS,2);

   hwma0=iMA(_Symbol,PERIOD_CURRENT,2,ma_shift,ma_method,applied_price);
   if(hwma0==INVALID_HANDLE)
     {
      //--- tell about the failure and output the error code
      PrintFormat("Failed to create handle of the iMA indicator for the symbol %s/%s, error code %d",
                  _Symbol,
                  EnumToString(PERIOD_CURRENT),
                  GetLastError());
      //--- the indicator is stopped early, if the returned value is negative
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int nLimit;

   if(rates_total!=prev_calculated)
     {
      CopyBuffer(hwma0,0,0,rates_total-prev_calculated+1,wma0);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma0,wma1,wma1weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma1,wma2,wma2weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma2,wma3,wma3weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma3,wma4,wma4weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma4,wma5,wma5weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma5,wma6,wma6weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma6,wma7,wma7weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma7,wma8,wma8weightsum);
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,2,wma8,wma9,wma9weightsum);

      if(prev_calculated==0) nLimit=rates_total-1;
      else nLimit=rates_total-prev_calculated+1;
      
      for(int i=nLimit; i>=0; i--)
         rainbow[i]=(5*wma0[i]+4*wma1[i]+3*wma2[i]+2*wma3[i]+wma4[i]+wma5[i]+wma6[i]+wma7[i]+wma8[i]+wma9[i])/20.0;

      iRSIOnArray(rates_total,prev_calculated,11,RSIPeriod,rainbow,rsi,bufpos,bufneg);

      ExponentialMAOnBuffer(rates_total,prev_calculated,12,EMAPeriod,rsi,ema0);
      ExponentialMAOnBuffer(rates_total,prev_calculated,13,EMAPeriod,ema0,ema1);

      for(int i=nLimit; i>=0; i--)
         srsi[i]=ema0[i]+(ema0[i]-ema1[i]);

      for(int i=nLimit; i>=0; i--)
         fish[i]=((MathExp(2*srsi[i])-1)/(MathExp(2*srsi[i])+1)+1)*50;         
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
///                        Calculating RSI
//+------------------------------------------------------------------+
int iRSIOnArray(const int rates_total,const int prev_calculated,const int begin,
                const int period,const double &price[],double &buffer[],double &bpos[],double &bneg[])
  {
   int        i;
//--- check for data
   ArrayResize(bneg,rates_total);
   ArrayResize(bpos,rates_total);

   if(period<=1 || rates_total-begin<period) return(0);
//--- save as_series flags
   bool as_series_price=ArrayGetAsSeries(price);
   bool as_series_buffer=ArrayGetAsSeries(buffer);
   if(as_series_price) ArraySetAsSeries(price,false);
   if(as_series_buffer) ArraySetAsSeries(buffer,false);

   double diff=0.0;
//--- check for rates count
   if(rates_total<=period)
      return(0);
//--- preliminary calculations
   int ppos=prev_calculated-1;
   if(ppos<=begin+period)
     {
      //--- first RSIPeriod values of the indicator are not calculated
      for (i=0; i<begin; i++)
      {
      buffer[i]=0.0;
      bpos[i]=0.0;
      bneg[i]=0.0;
      }
      double SumP=0.0;
      double SumN=0.0;
      for(i=begin;i<=begin+period;i++)
        {
         buffer[i]=0.0;
         bpos[i]=0.0;
         bneg[i]=0.0;
         //PrintFormat("%f %f\n", price[i], price[i-1]);
         diff=price[i]-price[i-1];
         SumP+=(diff>0?diff:0);
         SumN+=(diff<0?-diff:0);
        }
      //--- calculate first visible value
      bpos[begin+period]=SumP/period;
      bneg[begin+period]=SumN/period;
      if (bneg[begin+period]>0.0000001)
      buffer[begin+period]=0.1*((100.0-100.0/(1+bpos[begin+period]/bneg[begin+period]))-50);
      //--- prepare the position value for main calculation
      ppos=begin+period+1;
     }
//--- the main loop of calculations

   for(i=ppos;i<rates_total && !IsStopped();i++)
     {
      diff=price[i]-price[i-1];
      bpos[i]=(bpos[i-1]*(period-1)+((diff>0.0)?(diff):0.0))/period;
      bneg[i]=(bneg[i-1]*(period-1)+((diff<0.0)?(-diff):0.0))/period;
      if (bneg[i]>0.0000001)
      buffer[i]=0.1*((100.0-100.0/(1+bpos[i]/bneg[i]))-50);
      //Print(buffer[i]);
     }
//--- restore as_series flags
   if(as_series_price) ArraySetAsSeries(price,true);
   if(as_series_buffer) ArraySetAsSeries(buffer,true);

   return(rates_total);
  }
//+------------------------------------------------------------------+
    

Indicador inverso de Fisher

Figura 9. Indicador de transformada inversa de Fisher

Porque eu simplesmente apresentei as equações das transformadas, você pode estar curioso para saber a origem da transformada e da transformada inversa de Fisher.

Quando eu pesquisava materiais para escrever o artigo, fiquei interessado em como Fisher obteve as duas transformadas, mas não encontrei nada na Internet.

Porém, quando observei as duas transformadas, os dois gráficos me lembraram de algum tipo de função trigonométrica ou hiperbólica (você consegue notar alguma semelhança?). Porque essas funções podem ser derivadas da fórmula de Euler e expressas em termos do número "e" de Euler, eu retornei aos livros de cálculo e verifiquei isto novamente:

Figura 9. Equação senh,

Figura 11. Equação cosh,

e como agora sabemos disso, a tanh pode ser obtida por:

Figura 12. Equação tanh,

e...

Figura 12. Equação tanh inversa

Sim, essas são exatamente as mesmas equações que apresentei acima. A transformada de Fisher foi desmistificada! A transformada de Fisher é simplesmente a arctanh(x) e a transformada inversa de Fisher é o seu inverso, a tanh(x)!


6. Módulo de sinais de negociação

Para verificação da transformada inversa de Fisher, eu criei um módulo de sinais de negociação baseado no indicador de transformada inversa de Fisher.

Você pode considerar útil observar o módulo de negociação com base em um indicador personalizado. Eu utilizei a instância de classe CiCustom para segurar o indicador inverso de Fisher e substituí quatro métodos virtuais da classe CExpertSignal: CheckOpenLong() e CheckOpenShort() são responsáveis por gerar sinais quando não há posições abertas e CheckReverseLong() e CheckReverseShort() são responsáveis por reverter posições abertas.

//+------------------------------------------------------------------+
//|                               InverseFisherRSISmoothedSignal.mqh |
//|                                    Copyright © 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//|                                                      Version v01 |
//+------------------------------------------------------------------+
#property tester_indicator "SmoothedRSIInverseFisherTransform.ex5"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| Class CSignalInverseFisherRSISmoothed.                           |
//| Description: Class generating InverseFisherRSISmoothed signals   |
//|              Derived from CExpertSignal.                         |
//+------------------------------------------------------------------+

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signal on the Inverse Fisher RSI Smoothed Indicator        |
//| Type=SignalAdvanced                                              |
//| Name=InverseFisherRSISmoothed                                    |
//| Class=CSignalInverseFisherRSISmoothed                            |
//| Page=                                                            |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| CSignalInverseFisherRSISmoothed class                            |
//| Purpose: A class of a module of trade signals,                   |
//| on InverseFisherRSISmoothed                                      |
//+------------------------------------------------------------------+
class CSignalInverseFisherRSISmoothed : public CExpertSignal
  {
protected:
   CiCustom          m_invfish;
   double            m_stop_loss;
   
public:
                     CSignalInverseFisherRSISmoothed();
   //--- methods initialize protected data
   virtual bool      InitIndicators(CIndicators *indicators);
   virtual bool      ValidationSettings();
   //---
   virtual bool      CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckReverseLong(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckReverseShort(double &price,double &sl,double &tp,datetime &expiration);
   
protected:
   bool              InitInvFisher(CIndicators *indicators);
   double            InvFish(int ind) { return(m_invfish.GetData(0,ind)); }
  };
//+------------------------------------------------------------------+
//| Constructor CSignalInverseFisherRSISmoothed.                                    |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSignalInverseFisherRSISmoothed::CSignalInverseFisherRSISmoothed()
  {
//--- initialize protected data
  }
//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//| INPUT:  no.                                                      |
//| OUTPUT: true-if settings are correct, false otherwise.           |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::ValidationSettings()
  {
//--- initial data checks
 if(!CExpertSignal::ValidationSettings()) return(false);
//--- ok
   return(true);
  }
  
//+------------------------------------------------------------------+
//| Create Inverse Fisher custom indicator.                          |
//| INPUT:  indicators -pointer of indicator collection.             |
//| OUTPUT: true-if successful, false otherwise.                     |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
  bool CSignalInverseFisherRSISmoothed::InitInvFisher(CIndicators *indicators)
  {
//--- check pointer
   printf(__FUNCTION__+": initializing Inverse Fisher Indicator");
   if(indicators==NULL) return(false);
//--- add object to collection
   if(!indicators.Add(GetPointer(m_invfish)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
     MqlParam invfish_params[];
   ArrayResize(invfish_params,2);
   invfish_params[0].type=TYPE_STRING;
   invfish_params[0].string_value="SmoothedRSIInverseFisherTransform";
   //--- applied price
   invfish_params[1].type=TYPE_INT;
   invfish_params[1].integer_value=PRICE_CLOSE;
//--- initialize object
   if(!m_invfish.Create(m_symbol.Name(),m_period,IND_CUSTOM,2,invfish_params))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
   m_invfish.NumBuffers(18);
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Create indicators.                                               |
//| INPUT:  indicators -pointer of indicator collection.             |
//| OUTPUT: true-if successful, false otherwise.                     |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::InitIndicators(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL) return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- create and initialize SAR indicator
   if(!InitInvFisher(indicators)) return(false);
   m_stop_loss = 0.0010;
//--- ok
   printf(__FUNCTION__+": all inidicators properly initialized.");
   return(true);
  }
//+------------------------------------------------------------------+
//| Check conditions for long position open.                         |
//| INPUT:  price      - reference for price,                        |
//|         sl         - reference for stop loss,                    |
//|         tp         - reference for take profit,                  |
//|         expiration - reference for expiration.                   |
//| OUTPUT: true-if condition performed, false otherwise.            |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration)
  {
   printf(__FUNCTION__+" checking signal");
   
   int idx=StartIndex();
   
//---
   price=0.0;
   tp   =0.0;
//---
   if(InvFish(idx+2)<12.0 && InvFish(idx+1)>12.0)
   { 
      printf(__FUNCTION__ + " BUY SIGNAL");
      return true;
   } else printf(__FUNCTION__ + " NO SIGNAL");
//---
   return false;
  }
//+------------------------------------------------------------------+
//| Check conditions for long position close.                        |
//| INPUT:  price - refernce for price.                              |
//| OUTPUT: true-if condition performed, false otherwise.            |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::CheckReverseLong(double &price,double &sl,double &tp,datetime &expiration)
  {
   long tickCnt[1];
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if (ticks!=1 || tickCnt[0]!=1) return false;
   
   int idx=StartIndex();
   
   price=0.0;
// sl   =m_symbol.NormalizePrice(m_symbol.Bid()+20*m_stop_level);
//---
   
   if((InvFish(idx+1)>88.0 && InvFish(idx)<88.0)  || 
     (InvFish(idx+2)>88.0 && InvFish(idx+1)<88.0) ||
     (InvFish(idx+2)>12.0 && InvFish(idx+1)<12.0))
  {
   printf(__FUNCTION__ + " REVERSE LONG SIGNAL");
   return true;
   } else printf(__FUNCTION__ + " NO SIGNAL");
   return false;
  }
//+------------------------------------------------------------------+
//| Check conditions for short position open.                        |
//| INPUT:  price      - refernce for price,                         |
//|         sl         - refernce for stop loss,                     |
//|         tp         - refernce for take profit,                   |
//|         expiration - refernce for expiration.                    |
//| OUTPUT: true-if condition performed, false otherwise.            |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration)
  {
   printf(__FUNCTION__+" checking signal");
   int idx=StartIndex();
//---
   price=0.0;
   sl   = 0.0;
//---
   if(InvFish(idx+2)>88.0 && InvFish(idx+1)<88.0)
   {printf(__FUNCTION__ + " SELL SIGNAL");
      return true;} else printf(__FUNCTION__ + " NO SIGNAL");
      
//---
   return false;
  }
//+------------------------------------------------------------------+
//| Check conditions for short position close.                       |
//| INPUT:  price - refernce for price.                              |
//| OUTPUT: true-if condition performed, false otherwise.            |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CSignalInverseFisherRSISmoothed::CheckReverseShort(double &price,double &sl,double &tp,datetime &expiration)
  {
   long tickCnt[1];
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if (ticks!=1 || tickCnt[0]!=1) return false;
   
   int idx=StartIndex();
  
   price=0.0;
//---
   
   if((InvFish(idx+1)<12.0 && InvFish(idx)>12.0) ||
    (InvFish(idx+2)<12.0 && InvFish(idx+1)>12.0) ||
    (InvFish(idx+2)<88.0 && InvFish(idx+1)>88.0)) 
  {
   printf(__FUNCTION__ + " REVERSE SHORT SIGNAL");
   return true;
   } else printf(__FUNCTION__ + " NO SIGNAL");
   return false;
  }
    


7. Expert Advisor

Para verificação da transformada inversa de Fisher, eu criei um EA padrão que utiliza o módulo de sinais de negociação apresentado anteriormente.

Além disso, adicionei o módulo de rastreamento de ordem do tipo stop, extraído do artigo "MQL5 Wizard: Como criar um módulo de rastreamento de posições abertas".

//+------------------------------------------------------------------+
//|                                                 InvRSIFishEA.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\Signal\MySignal\InverseFisherRSISmoothedSignal.mqh>
//--- available trailing
#include <Expert\Trailing\SampleTrailing.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string Expert_Title         ="InvRSIFishEA";   // Document name
ulong        Expert_MagicNumber   =7016; // 
bool         Expert_EveryTick     =true; // 
//--- inputs for main signal
input int    Signal_ThresholdOpen =10;    // Signal threshold value to open [0...100]
input int    Signal_ThresholdClose=10;    // Signal threshold value to close [0...100]
input double Signal_PriceLevel    =0.0;   // Price level to execute a deal
input double Signal_StopLevel     =0.0;   // Stop Loss level (in points)
input double Signal_TakeLevel     =0.0;   // Take Profit level (in points)
input int    Signal_Expiration    =0;    // Expiration of pending orders (in bars)
input double Signal__Weight       =1.0;   // InverseFisherRSISmoothed Weight [0...1.0]
//--- inputs for money
input double Money_FixLot_Percent =10.0;  // Percent
input double Money_FixLot_Lots    =0.2;   // Fixed volume
//+------------------------------------------------------------------+
//| Global expert object                                             |
//+------------------------------------------------------------------+
CExpert ExtExpert;
//+------------------------------------------------------------------+
//| Initialization function of the expert                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initializing expert
   if(!ExtExpert.Init(Symbol(),Period(),Expert_EveryTick,Expert_MagicNumber))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(-1);
     }
//--- Creating signal
   CSignalInverseFisherRSISmoothed *signal=new CSignalInverseFisherRSISmoothed;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(-2);
     }
//---
   ExtExpert.InitSignal(signal);
   signal.ThresholdOpen(Signal_ThresholdOpen);
   signal.ThresholdClose(Signal_ThresholdClose);
   signal.PriceLevel(Signal_PriceLevel);
   signal.StopLevel(Signal_StopLevel);
   signal.TakeLevel(Signal_TakeLevel);
   signal.Expiration(Signal_Expiration);

//--- Creation of trailing object
   CSampleTrailing *trailing=new CSampleTrailing;
   trailing.StopLevel(0);
   trailing.Profit(20);
   
   if(trailing==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating trailing");
      ExtExpert.Deinit();
      return(-4);
     }
//--- Add trailing to expert (will be deleted automatically))
   if(!ExtExpert.InitTrailing(trailing))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing trailing");
      ExtExpert.Deinit();
      return(-5);
     }
//--- Set trailing parameters
//--- Creation of money object
   CMoneyFixedLot *money=new CMoneyFixedLot;
   if(money==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating money");
      ExtExpert.Deinit();
      return(-6);
     }
//--- Add money to expert (will be deleted automatically))
   if(!ExtExpert.InitMoney(money))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing money");
      ExtExpert.Deinit();
      return(-7);
     }
//--- Set money parameters
   money.Percent(Money_FixLot_Percent);
   money.Lots(Money_FixLot_Lots);
//--- Check all trading objects parameters
   if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(-8);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(-9);
     }
//--- ok
   return(0);
  }
//+------------------------------------------------------------------+
//| Deinitialization function of the expert                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtExpert.Deinit();
  }
//+------------------------------------------------------------------+
//| "Tick" event handler function                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   ExtExpert.OnTick();
  }
//+------------------------------------------------------------------+
//| "Trade" event handler function                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   ExtExpert.OnTrade();
  }
//+------------------------------------------------------------------+
//| "Timer" event handler function                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   ExtExpert.OnTimer();
  }
//+------------------------------------------------------------------+
    

Devo admitir que o EA não foi lucrativo para todos os ativos e períodos de tempo, mas eu o ajustei para dar ótimos resultados para o período EURUSD 1H.

Encorajo os leitores a tentar alterar o módulo de sinais e as configurações do indicador. Você poderá encontrar um EA mais lucrativo do que o que foi apresentado neste artigo.

Gráfico do EA

Figura 10. EA da transformada inversa de Fisher

Resultado do EA

Figura 11. Gráfico de equilíbrio do EA da transformada inversa de Fisher


Conclusão

Espero que este artigo tenha apresentado uma boa introdução à transformada e à transformada inversa de Fisher e que tenha mostrado uma forma de construir um módulo de sinais de negociação com base em um indicador personalizado.

Eu utilizei o indicador RSI suavizado de transformada inversa de Fisher de Sylvain Vervoort, mas, na realidade, você pode facilmente aplicar a transformada inversa de Fisher a qualquer oscilador e construir o EA com base neste artigo.

Também encorajo os leitores a ajustar as configurações para criar EAs lucrativos com base no EA apresentado por mim. Abaixo, forneço os links externos para mais referências.


Referências

  1. Transformada de Fisher
  2. Utilizando a transformada de Fisher
  3. Transformada inversa de Fisher
  4. RSI suavizado de transformada inversa de Fisher