Aplicação da transformada de Fisher e da transformada inversa de Fisher à análise de mercado no MetaTrader 5
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. 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:
- 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
- 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.
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):
Agora, vamos voltar à parte inferior da Figura 1:
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:
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:
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:
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. 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:
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. 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); } //+------------------------------------------------------------------+
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:
,
,
e como agora sabemos disso, a tanh pode ser obtida por:
,
e...
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.
Figura 10. EA da transformada inversa de Fisher
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
- Transformada de Fisher
- Utilizando a transformada de Fisher
- Transformada inversa de Fisher
- RSI suavizado de transformada inversa de Fisher
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/303
- 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