Application de la transformation de Fisher et de la transformation inverse de Fisher à l'analyse des marchés dans MetaTrader 5
Introduction
L'article suivant présente la transformation de Fisher et la transformation inverse de Fisher appliquées aux marchés financiers.
La théorie de la transformation de Fisher est mise en pratique en mettant en œuvre la version MQL5 de l'indicateur Smoothed RSI Inverse Fisher Transform présenté dans le numéro d'octobre 2010 du magazine « Actions et produits de base ». La rentabilité de l'indicateur est backtestée par Expert Advisor qui utilise des signaux basés sur l'indicateur Fisher.
L'article est basé sur des livres et des articles de JFEhlers trouvés sur Internet. Toutes les références sont mentionnées en fin d'article.
1. PDF gaussien vs cycles de marché
Une hypothèse courante est que les prix ont une fonction de densité de probabilité normale.
Cela signifie que les écarts de prix par rapport à la moyenne peuvent être décrits comme une cloche gaussienne bien connue :
Figure 1. Cloche gaussienne
J'ai mentionné la fonction de densité de probabilité normale. Pour bien comprendre cela, introduisons plusieurs idées et formules mathématiques, j'espère qu'elles seront toutes compréhensibles pour la majorité des lecteurs.
Chercher Merriam-Webster, dictionnaire probabilité est définie comme
- Le rapport du nombre de résultats dans un ensemble exhaustif de résultats également probables qui produisent un événement donné au nombre total de résultats possibles ou
- La chance qu'un événement donné se produise.
Une variable aléatoire est une variable dont la valeur résulte d'une mesure sur un certain type de processus aléatoire. Dans notre cas, la variable aléatoire est le prix d'un actif.
Enfin, PDF est l'acronyme de Probability Density Function (fonction de densité de probabilité) - une fonction qui décrit la probabilité qu'une variable aléatoire X (encore une fois - dans notre cas le prix) prenne une valeur dans un certain intervalle de valeurs possibles. Une valeur de variable aléatoire qui résulte d'une distribution gaussienne ou d'une distribution normale est une distribution de probabilité qui est souvent utilisée pour décrire des variables aléatoires du monde réel qui ont tendance à se regrouper autour d'une seule valeur moyenne.
Mathématiquement parlant, la probabilité que la variable aléatoire X prenne la valeur qui se situe dans l'intervalle [a,b] est définie comme intégrale :
Cela représente l'aire sous la courbe f(x) de a à b. La probabilité est comptée de 0 à 100 % ou de 0 à 1,00, il y a donc une limite selon laquelle l'aire totale sous la courbe f(x) doit être égale à 1 (somme des probabilités) :
Revenons maintenant à la partie inférieure de la figure 1 :
Figure 2. Ecarts types en cloche gaussienne
Vous pouvez voir ici quel pourcentage de valeurs se situe sous la moyenne +/- 1-3 écarts types (sigmas). Avec la PDF gaussien, 68,27 % des occurrences se situent à plus/moins un écart-type par rapport à la moyenne, 95,45 % se situent à plus/moins deux écarts-types et 99,73 % se situent à plus/moins trois écarts-types par rapport à la moyenne.
Pensez-vous que c'est le cas avec les données de marché réelles ? Pas tout à fait. Lorsque nous examinons les prix du marché, nous pouvons supposer que le graphique ressemble à une vague carrée - après avoir franchi les niveaux de résistance ou de support où les commandes importantes sont groupées, les prix ont tendance à augmenter ou à baisser jusqu'au prochain niveau de support/résistance. C'est pourquoi le marché peut être modélisé avec une grande approximation comme une vague carrée ou sinusoïdale.
Veuillez observer le diagramme sinusoïdal ci-dessous :
Figure 3. Diagramme sinusoïdal
Vous devriez remarquer qu'en réalité, la plupart des transactions sont placées de la même manière près des niveaux de support et de résistance, ce qui semble assez naturel. Maintenant, je vais tracer le graphique de la densité d'une onde sinusoïdale. Vous pourriez imaginer que nous tournions la figure 3 de 90 degrés vers la droite et que tous les cercles qui font tomber le tracé tombent au sol :
Figure 4. Diagramme de densité de courbe sinusoïdale
Vous pouvez remarquer que la densité est plus élevée sur les positions les plus à gauche et les plus à droite. Cela semble être en accord avec l'affirmation précédente selon laquelle la plupart des transactions sont effectuées très près des niveaux de résistance et de support. Vérifions le pourcentage d'occurrences en dessinant un histogramme :
Figure 5. Histogramme de densité de courbe sinusoïdale
Est-ce que ça ressemble à une cloche gaussienne ? Pas exactement. Les trois premières et dernières mesures semblent avoir le plus d'occurrences.
J.F. Dans son livre « Analyse cybernétique des actions et des contrats à terme » Ehlers décrit une expérience où il a analysé des T-Bonds américains sur une période de 15 ans. Il a appliqué un canal normalisé de 10 barres de long et mesuré l'emplacement du prix dans les 100 cases et a compté le nombre de fois où le prix était dans chaque case. Les résultats de cette distribution de probabilité rappellent étroitement ceux d'une onde sinusoïdale.
2. La transformation de Fisher et son application aux séries chronologiques
Puisque nous savons maintenant que la PDF d'un cycle de marché ne rappelle pas une Gaussienne mais plutôt une PDF d'une onde sinusoïdale et que la plupart des indicateurs supposent que la PDF du cycle de marché est Gaussienne, nous avons besoin d'un moyen de « corriger » cela. La solution consiste à utiliser la transformation de Fisher. La transformation de Fisher change la PDF de n'importe quelle forme d'onde en une forme approximativement gaussienne.
L'équation de la transformation de Fisher est :
,
Figure 6. Transformation de Fisher
J'ai mentionné que la sortie de la transformée de Fisher est approximativement une PDF gaussienne. Pour expliquer cela, il convient d'examiner la figure 6.
Lorsque les données d'entrée sont proches de sa moyenne, le gain est approximativement égal à l'unité (voir le graphique pour |X<0,5|). D'autre part, lorsque l'entrée normalisée s'approche de l'une ou l'autre limite, la sortie est fortement amplifiée (voir le graphique pour 0,5<|x|<1). Dans la pratique, on peut penser à une queue « presque gaussienne », croissante, lorsque les écarts sont les plus importants - c'est exactement ce qui se passe avec la PDF transformée.
Comment appliquer la transformation de Fisher au trading ? Dans un premier temps, en raison de la contrainte |x|<1, les prix doivent être normalisés dans cet intervalle. Lorsque les prix normalisés sont soumis à la transformation de Fisher, les mouvements de prix extrêmes deviennent relativement rares. Cela signifie que la transformation de Fisher capte ces mouvements de prix extrêmes et nous permet de faire du trading en fonction de ces extrêmes.
3. La transformation de Fisher dans MQL5
Le code source de l'indicateur de la transformation de Fisher est décrit dans le livre de Ehlers « Cybernetic Analysis for Stocks and Futures ».
Il a déjà été mis en œuvre dans MQL4 et je l'ai converti en MQL5. L'indicateur utilise les prix médians (H+L)/2, j'ai utilisé la fonction iMA() pour extraire les prix médians de l'historique.
Dans un premier temps, les prix sont normalisés dans un intervalle de 10 barres et les prix normalisés sont soumis à la transformation 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); } //+------------------------------------------------------------------+
Veuillez noter que des signaux aigus sont générés.
La ligne de signal est simplement le prix transformé de Fisher retardé d'une barre :
Figure 7. Indicateur de transformation de Fisher
4. Transformation de Fisher inverse et son application aux indicateurs de cycle
L'équation de la transformation de Fisher inverse est obtenue en résolvant l'équation de la transformation de Fisher pour x en fonction de y :
,
Figure 8. Transformation de Fisher inverse
La réponse de transfert de cette fonction est inverse de celle de la transformation de Fisher.
Pour |x|>2 l'entrée est compressée pour ne pas dépasser l'unité (pour les nombres négatifs -1 et pour les positifs +1) et pour |x|<1 c'est une relation presque linéaire qui signifie que la sortie a plus moins les mêmes caractéristiques que l'entrée.
Le résultat est que lorsque la transformation de Fisher inverse est appliquée à des données d'entrée correctement préparées, la sortie a de grandes chances d'être -1 ou +1. Cela rend la transformation de Fisher inverse parfaite pour l'appliquer aux indicateurs d'oscillateur. La transformation de Fisher inverse peut les améliorer en donnant des signaux d'achat ou de vente précis.
5. Exemple de transformation de Fisher inverse dans MQL5
Afin de vérifier la transformation de Fisher inverse, j'ai mis en œuvre la version MQL5 de l'indicateur Vervoort Smoothed RSI transformation de Fisher inverse de Sylvain présenté dans le numéro d'octobre 2010 du magazine «Stocks and Commodities » et j'ai construit un module de signal de trading et Expert Advisor basé sur cet indicateur.
L'indicateur de transformation de Fisher inverse a déjà été mis en œuvre pour de nombreuses plateformes de trading, les codes sources sont disponibles sur les sites web traders.com site web et MQL5.com Code Base.
Comme il n'y avait pas de fonction irSIOnArray dans MQL5, je l'ai ajoutée au code indicateur. La seule différence avec l'indicateur original est la période RSIP fixée à 21 et la période EMAP fixée à 34, car il se comporte mieux pour mes paramètres (EURUSD 1H). Vous pouvez le changer par défaut pour RSIPeriod 4 et 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); } //+------------------------------------------------------------------+
Figure 9. Indicateur de transformation de Fisher inverse
Étant donné que je n'ai présenté que des équations de transformation, vous pourriez être intrigué par les origines de la transformation de Fisher et de la transformation de Fisher inverse.
Lorsque j'ai rassemblé les documents nécessaires à la rédaction de l'article, je me suis intéressé à la manière dont Fisher avait obtenu ces deux transformations, mais je n'ai rien trouvé sur Internet.
Mais j'ai regardé la transformation de Fisher et la transformation de Fisher inverse et les deux graphiques m'ont fait penser à une sorte de fonction trigonométrique ou hyperbolique (voyez-vous des similitudes ?). Étant donné que ces fonctions peuvent être dérivées de la formule d'Euler et exprimées en termes de nombre « e » d'Euler, je suis revenu aux livres de calcul et j'ai vérifié que :
,
,
et puisque nous savons maintenant que tanh(x) peut être obtenu par :
,
et...
Oui, ce sont exactement les mêmes équations que j'ai présentées ci-dessus. La transformation de Fisher démystifiée ! La transformation de Fisher est simplement arctanh(x) et la transformation de Fisher inverse est son inverse, tanh(x) !
6. Module de signaux de trading
Afin de vérifier la transformation inverse de Fisher, j’ai construit un module de signal de trading basé sur l'indicateur de transformation inverse de Fisher.
Vous trouverez peut-être utile de voir le module de trading basé sur un indicateur personnalisé. J'ai utilisé l'instance de classe CiCustom pour contenir l'indicateur de Fisher Inverse et remplacé quatre méthodes virtuelles de la classe CExpertSignal : CheckOpenLong() et CheckOpenShort() sont responsables de la génération de signaux lorsqu'il n'y a pas de position ouverte et CheckReverseLong() et CheckReverseShort() sont responsables de l'inversion de la position ouverte.
//+------------------------------------------------------------------+ //| 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
Afin de vérifier la transformation inverse de Fisher, j’ai construit un EA standard qui utilise le module de signal de trading présenté précédemment.
J'ai également ajouté un module trailing stop-loss tiré de l'article « MQL5 Wizard : Comment créer un module de suivi des positions ouvertes ».
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Je dois admettre que l'EA n'était pas rentable pour chaque actif et pour chaque période, mais je l'ai modifié pour donner de très bons résultats pour la période EURUSD 1H.
J'encourage les lecteurs à essayer de modifier le module de signal et les paramètres de l'indicateur, vous trouverez peut-être un EA plus rentable que celui présenté dans l'article.
Figure 10. EA transformation de Fisher inverse
Figure 11. Graphique d'équilibre EA de la transformation de Fisher inverse
Conclusion
J'espère que cet article a fourni une bonne introduction à la transformation de Fisher et à la transformation de Fisher inverse et a montré une façon de construire un module de trading de signaux basé sur un indicateur personnalisé.
J'ai utilisé l'indicateur RSI Vervoort Smoothed transformation de Fisher inverse de Sylvain, mais en fait vous pouvez facilement appliquer la transformation de Fisher inverse à n'importe quel oscillateur et construire un EA sur la base de cet article.
J'encourage également les lecteurs à modifier les paramètres pour créer un EA rentable basé sur celui que j'ai présenté. J’ai fournis des liens externes pour une référence supplémentaire ci-dessous.
Références
- La transformation de Fisher
- Utilisation de la transformation de Fisher
- La transformation de Fisher inverse
- transformation de Fisher inverse RSI lissée
Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/303
- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation