English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Applicazione della trasformata di Fisher e della trasformata inversa di Fisher all'analisi dei mercati su MetaTrader 5

Applicazione della trasformata di Fisher e della trasformata inversa di Fisher all'analisi dei mercati su MetaTrader 5

MetaTrader 5Trading | 11 gennaio 2022, 15:11
419 0
investeo
investeo

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. Distribuzione gaussiana 

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

  1. 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
  2. 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:

Figura 2. Densità di probabilità 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à):

Figura 3. Superficie totale sotto curva

Ora torniamo alla parte inferiore della Figura 1:

Figura 4. Parte inferiore della figura gaussiana 

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:

seno

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: 

Densità 

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:

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. Equazione della trasformata di Fisher

 Figura 5. 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:

Indicatore della trasformata di Fisher 

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. Equazione della trasformata di Fisher inversa,

Figura 6. Trasformata di Fisher inversa 

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);
  }
//+------------------------------------------------------------------+

 Indicatore della trasformata inversa di Fisher

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:

Figura 9. Equazione di Sinh,

Figura 11. Equazione di Cosh

e poiché ora che tanh(x) può essere ottenuto da:

Figura 12. Equazione di Tanh,

e... 

Figura 12. Equazione di Atanh 

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.

Grafico EA 

Figura 10. EA della trasformata di Fisher inversa

Risultato EA

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

  1. The Fisher Transform
  2. Utilizzo della trasformata di Fisher
  3. La trasformata inversa di Fisher
  4. Smoothed RSI Inverse Fisher Transform

Tradotto dall’inglese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/en/articles/303

File allegati |
Come sviluppare un Expert Advisor utilizzando gli strumenti UML Come sviluppare un Expert Advisor utilizzando gli strumenti UML
Questo articolo discute la creazione di Expert Advisor utilizzando il linguaggio grafico UML, usato per la modellazione visiva di sistemi software orientati agli oggetti. Il vantaggio principale di questo approccio è la visualizzazione del processo di modellazione. L'articolo contiene un esempio che mostra la modellazione della struttura e delle proprietà di un Expert Advisor utilizzando il software Ideas Modeler.
Pagamenti e metodi di pagamento Pagamenti e metodi di pagamento
I servizi MQL5.community offrono grandi opportunità per i trader e per gli sviluppatori di applicazioni per il terminale MetaTrader. In questo articolo spieghiamo come vengono eseguiti i pagamenti per i servizi MQL5, come è possibile prelevare il denaro guadagnato e come viene garantita la sicurezza dell'operazione.
Come aggiungere nuove lingue dell'interfaccia utente alla piattaforma MetaTrader 5 Come aggiungere nuove lingue dell'interfaccia utente alla piattaforma MetaTrader 5
L'interfaccia utente della piattaforma MetaTrader 5 è tradotta in diverse lingue. Non preoccuparti se la tua lingua madre non è tra quelle supportate. Puoi facilmente completare la traduzione utilizzando la speciale utility MetaTrader 5 MultiLanguage Pack offerta da MetaQuotes Software Corp. gratuitamente a tutti i partecipanti. In questo articolo mostreremo alcuni esempi di come aggiungere una nuova interfaccia utente lingue alla piattaforma MetaTrader 5.
Utilizzo di WinInet in MQL5.  Parte 2:  Richieste e file POST Utilizzo di WinInet in MQL5. Parte 2: Richieste e file POST
In questo articolo, continuiamo a studiare i principi del lavoro con Internet utilizzando le richieste HTTP e lo scambio di informazioni con il server. Descrive nuove funzionalità della classe CMqlNet, modalità di invio di informazioni da form e invio di file tramite richieste POST nonché autorizzazione sui siti web con il tuo nome utente tramite Cookie.