
Applicazione della trasformata di Fisher e della trasformata inversa di Fisher all'analisi dei mercati su MetaTrader 5
Introduzione
Il seguente articolo presenta la trasformata di Fisher e la trasformata inversa di Fisher applicate ai mercati finanziari.
La teoria della trasformata di Fisher viene messa in pratica implementando la versione MQL5 dell'indicatore Smoothed RSI della trasformata inversa di Fisher presentato nel numero di ottobre 2010 della rivista "Stocks and Commodities". La redditività dell'indicatore viene testata da un Expert Advisor che utilizza segnali basati sull'indicatore Fisher.
L'articolo è basato su libri e articoli di J.F.Ehlers reperiti su Internet. Tutti i riferimenti sono menzionati alla fine dell'articolo.
1. PDF gaussiano vs Cicli di mercato
Un'ipotesi comune è che i prezzi abbiano una normale funzione di densità di probabilità.
Ciò significa che le deviazioni di prezzo dalla media possono essere descritte come una campana gaussiana ben nota:
Figura 1. Campana gaussiana
Ho menzionato la normale funzione di densità di probabilità. Per capirla appieno, introduciamo diverse idee e formule matematiche che spero siano tutte comprensibili per la maggior parte dei lettori.
“Going after probability nel dizionario Merriam-Webster è definito come
- Il rapporto tra il numero di risultati in un insieme esaustivo di risultati ugualmente probabili che producono un determinato evento e il numero totale di possibili risultati o
- la possibilità che si verifichi un determinato evento.
Una variabile casuale è una variabile il cui valore risulta da una misurazione su un qualche tipo di processo casuale. Nel nostro caso, la variabile casuale è un prezzo di un asset.
Infine, PDF è l'acronimo di Probability Density Function - una funzione che descrive la probabilità che una variabile casuale X (di nuovo, nel nostro caso, prezzo) assuma un valore in un certo intervallo di valori possibili. Un valore variabile casuale che risulta da una distribuzione gaussiana o distribuzione normale è una distribuzione di probabilità che viene spesso utilizzata per descrivere variabili casuali del mondo reale che tendono a raggrupparsi attorno a un singolo valore medio.
Matematicamente parlando, la probabilità che la variabile casuale X assuma il valore thal e si trovi nell'intervallo [a,b] è definita come integrale:
Rappresenta l'area sotto la curva f(x) da a a b. La probabilità viene contata da 0 a 100% o da 0 a 1,00, quindi c'è un limite per cui l'area totale sotto la curva f(x) deve essere uguale a 1 (somma delle probabilità):
Ora torniamo alla parte inferiore della Figura 1:
Figura 2. Deviazioni standard della campana gaussiana
Puoi vedere qui quale percentuale di valori è sotto la media +/- 1-3 delle deviazioni standard (sigma). Con il PDF gaussiano, il 68,27% delle occorrenze rientra in più o meno in una deviazione standard dalla media, il 95,45% rientra in più o meno due deviazioni standard e il 99,73% rientra in più o meno tre deviazioni standard dalla media.
Pensi che sia il caso dei dati di mercato reali? Non proprio. Quando guardiamo ai prezzi di mercato possiamo piuttosto supporre che il grafico assomigli a un'onda quadra - dopo aver violato i livelli di resistenza o supporto in cui sono raggruppati grandi ordini, i prezzi tendono a salire o scendere al livello di supporto / resistenza successivo. Ecco perché il mercato può essere modellato con grande approssimazione come un quadrato o un'onda sinusoidale.
Osserva a trama sinusoidale di seguito:
Figura 3. Trama sinusoidale
Dovresti notare che in realtà la maggior parte delle negoziazioni sono posizionate in modo simile vicino ai livelli di supporto e resistenza, il che sembra abbastanza naturale. Ora trascinerò la trama di densità di un'onda sinusoidale. Puoi immaginare che stiamo ruotando la Figura 3 di 90 gradi a destra e che lasciamo cadere a terra tutti i cerchi che formano la trama:
Figura 4. Grafico della densità della curva sinusoidale
Potresti notare che la densità è più alta nelle posizioni più a sinistra e più a destra. Questo sembra essere in linea con la precedente affermazione che la maggior parte delle negoziazioni sono fatte molto vicino ai livelli di resistenza e supporto. Controlliamo quale percentuale di occorrenze emergono disegnando un istogramma:
Figura 5. Istogramma della densità della curva sinusoidale
Sembra una campana gaussiana? Non esattamente. La prima e le ultime tre barre sembrano avere la maggior parte delle occorrenze.
J.F. Ehlers nel suo libro "Сybernetic analysis for stocks and futures" ha descritto un esperimento in cui ha analizzato gli Stati Uniti. T-Bond nell'arco di 15 anni. Ha applicato un canale normalizzato lungo 10 barre, ha misurato la posizione del prezzo all'interno di 100 contenitori e ha contato il numero di volte in cui il prezzo era in ciascun contenitore. I risultati di questa distribuzione di probabilità ricordano da vicino quelli di un'onda sinusoidale.
2. La trasformata di Fisher e la sua applicazione alle serie temporali
Poiché ora sappiamo che il PDF di un ciclo di mercato non ricorda un ciclo gaussiano, ma piuttosto un PDF di un'onda sinusoidale e che la maggior parte degli indicatori presume che il ciclo di mercato PDF sia gaussiano, abbiamo bisogno di un modo per "correggerlo". La soluzione è utilizzare la trasformata di Fisher. La trasformata di Fisher cambia il PDF di qualsiasi forma d'onda in una curva approssimativamente gaussiana.
L'equazione per la trasformata di Fisher è:
,
Figura 6. Trasformata di Fisher
Ho detto che l'output della trasformata di Fisher è un PDF gaussiano. Per spiegare questo vale la pena guardare la Figura 6.
Quando i dati di input sono vicini alla media, il guadagno è approssimativamente l'unità (osserva il grafico per | X<0.5|). D'altra parte, quando l'input normalizzato si avvicina a entrambi i limiti, l'output è notevolmente amplificato (osserva il grafico per 0,5<|x|<1). In pratica, si potrebbe pensare di far crescere la coda "quasi gaussiana" quando si verificano le maggiori deviazioni: questo è esattamente ciò che accade al PDF trasformato.
Come applichiamo la trasformata di Fisher nel trading? Inizialmente, a causa del vincolo |x|<1, i prezzi devono essere normalizzati in questo intervallo. Quando i prezzi normalizzati sono sottoposti alla trasformata di Fisher, i movimenti estremi dei prezzi diventano relativamente rari. Ciò significa che la trasformata di Fisher cattura quei movimenti estremi di prezzo e ci consente di fare trading in base a quegli estremi.
3. La trasformata di Fisher in MQL5
Il codice sorgente dell'indicatore della trasformata di Fisher è descritto nel libro di Ehlers "Cybernetic Analysis for Stocks and Futures".
È già stato implementato in MQL4 e l'ho convertito in MQL5. L'indicatore utilizza i prezzi mediani (H + L) / 2. Io ho usato la funzione iMA () per estrarre i prezzi mediani dalla cronologia.
I primi prezzi sono normalizzati entro 10 barre e i prezzi normalizzati sono soggetti alla trasformata di 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); } //+------------------------------------------------------------------+
Si prega di notare che vengono generati segnali nitidi.
La linea di segnale è semplicemente il prezzo della trasformata di Fisher ritardato di una barra:
Figura 7. Indicatore della trasformata di Fisher
4. La trasformata inversa di Fisher e la sua applicazione agli indicatori di ciclo
L'equazione della trasformata di Fisher inversa si ottiene risolvendo l'equazione della trasformata di Fisher per x in termini di y:
,
Figura 8. Trasformata di Fisher inversa
La risposta di trasferimento di questa funzione è inversa a quella della trasformata di Fisher.
Per |x|>2 l'input è compresso per non superare l'unità (per i numeri negativi -1 e per il positivo +1) e per |x|<1 è una relazione quasi lineare, il che significa che l'output ha più meno le stesse caratteristiche dell'input.
Il risultato è che quando la trasformata di Fisher inversa viene applicata a dati di input correttamente preparati, l'output ha una grande possibilità di essere -1 o +1. Questo rende la trasformata inversa di Fisher perfetta per applicarla agli indicatori dell'oscillatore. La trasformata inversa di Fisher può migliorarli dando forti segnali di acquisto o vendita.
5. Esempio di trasformata di Fisher inversa in MQL5
Al fine di verificare la trasformata inversa di Fisher, ho implementato la versione MQL5 dell'indicatore Vervoort Smoothed RSI della trasformata inversa di Fisher di Sylvain presentato nel numero di ottobre 2010 della rivista "Stocks and Commodities" e ho costruito un modulo di segnale di trading e Expert Advisor basato su tale indicatore.
L'indicatore trasformata inversa di Fisher è già stato implementato per molte piattaforme di trading, i codici sorgente sono disponibili sul sito traders.com e MQL5.com Code Base.
Poiché non c'era alcuna funzione iRSIOnArray in MQL5, l'ho aggiunta al codice dell'indicatore. L'unica differenza con l'indicatore originale è RSIPeriod predefinito impostato su 21 e EMAPeriod impostato su 34, poiché si è comportato meglio per le mie impostazioni (EURUSD 1H). È possibile modificarlo in RSIPeriod 4 e EMAPeriod 4 predefiniti.
//+------------------------------------------------------------------+ //| 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. Indicatore della trasformata di Fisher inversa
Dal momento che ho presentato solo equazioni di trasformata, potresti essere perplesso sulle origini della trasformata di Fisher e della trasformata di Fisher inversa.
Quando stavo raccogliendo materiali per scrivere l'articolo, mi sono interessato a come Fisher ha ottenuto entrambe le trasformazioni, ma non ho trovato nulla su Internet.
Ma ho osservato sia la trasformata di Fisher che la trasformata di Fisher inversa ed entrambe le trame mi hanno ricordato una sorta di funzioni trigonometriche o iperboliche (riesci a vedere qualche somiglianza?). Poiché queste funzioni possono essere derivate dalla formula di Eulero ed espresse in termini di numero di Eulero 'e', ho ripreso i libri di calcolo e ho ricontrollato che:
,
e poiché ora che tanh(x) può essere ottenuto da:
,
e...
Sì, queste sono esattamente le stesse equazioni che ho presentato sopra. La trasformata di Fisher è demistificata! La trasformata di Fisher è semplicemente arctanh(x) e la trasformata di Fisher inversa è la sua inversa, tanh(x)!
6. Moduli dei segnali di trading
Per verificare la trasformata inversa di Fisher costruisco un modulo di segnale di trading basato sull'indicatore trasformata inversa di Fisher.
Potresti trovare utile vedere il modulo di trading basato su un indicatore personalizzato. Ho usato l'istanza della classe CiCustom per contenere l'indicatore della trasformata inversa di Fisher e ho sovrascritto quattro metodi virtuali della classe CExpertSignal: CheckOpenLong() e CheckOpenShort() sono responsabili della generazione di segnali quando non c'è una posizione aperta e CheckReverseLong() e CheckReverseShort() sono responsabili dell'inversione della posizione aperta.
//+------------------------------------------------------------------+ //| 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
Per verificare la trasformata inversa di Fisher costruisco un EA standard che utilizza il modulo del segnale di trading presentato in precedenza.
Ho anche aggiunto il modulo trailing stop-loss tratto dall'articolo "MQL5 Wizard: How to Create a Module of Trailing of Open Positions".
//+------------------------------------------------------------------+ //| 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 ammettere che l'EA non era redditizio per ogni asset e per ogni lasso di tempo, ma l'ho modificato per dare risultati abbastanza buoni per EURUSD 1H timeframe.
Incoraggio i lettori a provare a cambiare il modulo del segnale e le impostazioni dell'indicatore, potreste trovare EA più redditizi di quello presentato nell'articolo.
Figura 10. EA della trasformata di Fisher inversa
Figura 11. Grafico di bilanciamento dell’EA della trasformata di Fisher inversa
Conclusione
Spero che l'articolo abbia fornito una buona introduzione alla trasformata di Fisher e alla trasformata inversa di Fisher e che abbia mostrato un modo per costruire un modulo di trading del segnale basato su un indicatore personalizzato.
Ho usato l'indicatore Vervoort Smoothed RSI della trasformata inversa di Fisher di Sylvain, ma in realtà puoi facilmente applicare la trasformata inversa di Fisher a qualsiasi oscillatore e costruire EA basati su questo articolo.
Incoraggio anche i lettori a modificare le impostazioni per creare un EA redditizio basato su quello che ho presentato. Sto fornendo collegamenti esterni per ulteriori riferimenti di seguito.
Fonti
- The Fisher Transform
- Utilizzo della trasformata di Fisher
- La trasformata inversa di Fisher
- Smoothed RSI Inverse Fisher Transform
Tradotto dall’inglese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/en/articles/303





- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso