English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Anwendung der Fisher-Transformation und der umgekehrten Fisher-Transformation bei der Marktanalyse mit MetaTrader5

Anwendung der Fisher-Transformation und der umgekehrten Fisher-Transformation bei der Marktanalyse mit MetaTrader5

MetaTrader 5Handel | 4 Mai 2016, 15:19
2 833 0
investeo
investeo

Einleitung

Im folgenden Artikel werden die Fisher-Transformation und die umgekehrte Fisher-Transformation in Finanzmärkten angewandt.

Die Fisher-Transformationstheorie wird umgesetzt mit der Implementierung der MQL5-Version des Indikators "Smoothed RSI Inverse Fisher Transformation", der im Magazin "Stocks and Commodities" im Oktober 2010 vorgestellt wurde. Die Rentabilität des Indikators wird von einem Expert Advisor getestet, der auf den Fisher-Indikator basierende Signale verwendet.

Dieser Artikel beruft sich auf J.F. Ehlers Bücher und Internetartikel. Die Quellen werden am Ende des Artikels gelistet.


1. Gauß'sche PDF vs Marktzyklen

Es wird allgemein angenommen, dass Preise normale Wahrscheinlichkeitsdichtefunktionen haben.

Das heißt, dass Preisabweichungen von der Norm mit der wohlbekannten Gauß'schen Glockenkurve beschrieben werden können.

Abbildung 1 Gauß'sche Distribution 

Abbildung 1 Gauß'sche Glocke 

Ich habe die normale Wahrscheinlichkeitsdichtefunktion erwähnt. Um dies voll und ganz zu verstehen, stellen wir ein paar Ideen und mathematische Formeln vor, die hoffentlich für die Leser verständlich sind.

Das Merriam-Webster-Wörterbuch definiert probability folgendermaßen:

  1. Das Verhältnis der Anzahl von Ergebnissen in einem endlichen Satz an gleich wahrscheinlich möglichen Ergebnissen, die ein bestimmtes Ereignis auslösen, zur totalen Anzahl von möglichen Ergebnissen oder
  2. die Chance, dass sich ein bestimmtes Ereignis ereignen wird.

Eine zufällige Variable ist eine Variable, deren Wert das Ergebnis einer Messung eines zufälligen Prozesses ist. In diesem Fall ist die zufällige Variable der Preis eines Assets.  

PDF ist die Abkürzung des englischen "Probability Density Function", zu Deutsch, Wahrscheinlichkeitsdichtefunktion - eine Funktion, die die Wahrscheinlich angibt, mit der eine zufällige Variable X (in unserem Fall, der Preis) einen Wert in einem bestimmten Bereich möglicher Werte annimmt.  Der zufällige Wert einer Variable, der sich aus einer Gauß'schen Verteilung oder einer Normalverteilung ergibt, ist eine Wahrscheinlichkeitsvariable, die oft verwendet wird, um echte, zufällige Variablen zu beschreiben, die dazu tendieren, sich um einen einzigen Mittelwert zu versammeln.

Mathematisch gesprochen: die Wahrscheinlichkeit, dass die zufällige Variable X einen Wert annimmt, der im Intervall [a,b] liegt, wird als Integral definiert:

Abbildung 2 Integral der Wahrscheinlichkeitsdichte

Hier ist die Fläche unter der Kurve f(x) von a zu b dargestellt. Die Wahrscheinlichkeit wird von 0 bis 100% gezählt, oder von 0 bis 1,00, daher ist die Bedingung, dass die Gesamtfläche unter der f(x)-Kurve gleich 1 ist (die Summe der Wahrscheinlichkeiten).

Abbildung 3 Gesamtfläche unter der Kurve

Gehen wir nun zurück zum unteren Teil der Abbildung 1:

Abbildung 4 Unterer Teil der Gauß'schen Abbildung 

Abbildung 2 Gauß'sche Glocke Standardabweichung  

Hier können Sie sehen, wie viel Prozent der Werte unter +/- 1-3 der normalen Standardabweichung (sigma) sind. Mit der Gauß'schen PDF befinden sich 68,27% der Vorkommnisse im Bereich plus/minus der Standardabweichung des Mittelwertes, 95,45% befinden sich plus/minus zwei Standardabweichungen und 99,73/ befinden sich im Bereich von plus/minus drei Standardabweichungen vom Mittelwert.

Glauben Sie, dass dies bei echten Marktdaten auch so ist? Nicht ganz.  Wenn wir uns Marktpreise anschauen, können wir davon ausgehen, dass die Grafik wie eine Rechteckwelle ausschaut - nach der Überschreitung des Widerstandes oder unterstützten Ebenen, in denen große Bestellungen gesammelt werden, tendieren die Preise dazu, zu steigen oder zum nächsten unterstützten Level zu sinken. Deswegen können Märkte ungefähr als Rechteckwelle oder Sinuskurve dargestellt werden.

Beachten Sie die untere Sinus-Grafik.

Sinus

Abbildung 3 Sinus Grafik 

Sie sollten beachten, dass in Wirklichkeit die meisten Abschlüsse ähnlich nahe an den Widerstand- und Unterstützungslevels platziert sind, was normal erscheint. Ich werde nun die Dichte-Grafik einer Sinus-Kurve zeichnen. Stellen Sie sich vor, dass wir Abbildung 3 um 90° Grad nach rechts drehen und alle Kreise auf den Boden fallen lassen.  

Dichte 

Abbildung 4 Sinus-Kurve Dichte-Grafik  

Beachten Sie, dass die Dichte links und rechts außen am höchsten ist. Das scheint zu vorigen Aussagen zu passen, dass die meisten Abschlüsse sehr nahe an den Widerstands- und Unterstützungs-Levels sind. Überprüfen wir nun, wie viel Prozent Häufigkeit vorkommen, indem wir ein Histogramm zeichnen:

Histogramm

Abbildung 5 Sinus-Kurve Dichte-Histogramm

Schaut es wie eine Gauß'sche Glocke aus? Nicht wirklich. Die ersten und letzten Balken scheinen die größte Häufigkeit zu haben.

J.F. In seinem Buch "Сybernetic analysis for stocks and futures" (Kybernetische Analyse für Wertpapiere und Terminkontrakte) beschreibt Ehler ein Experiment, in dem US-T-Bonds über den Zeitraum von 15 Jahren analysiert wurden. Er verwendete einen genormten Kanal, zehn Balken lang, und maß die Preis-Lokation innerhalb 100 Positionen und zählte, wie oft der Preis in welcher Position war. Die Ergebnisse dieser Wahrscheinlichkeits-Verteilungs erinnern an eine Sinus-Kurve.  


2. Die Fisher-Transformation und ihre Anwendung an Zeitreihen

Wir wissen, dass die Wahrscheinlichkeitsdichtefunktion (PDF) eines Marktzyklus keine Gauß'sche Glockenkurve ist, sondern eher eine Sinuskurve. Da jedoch die meisten Indikatoren davon ausgehen, dass der Marktzyklus der Wahrscheinlichkeitsdichtefunktion die Gauß'sche Kurve ist, müssen wir das "korrigieren". Die Lösung ist die Fisher-Transformation. Die Fisher-Transformation verwandelt Wahrscheinlichkeitsdichtefunktionen jeder Wellenform ungefähr in die Gauß'sche Glocke.

Die Gleichung der Fisher-Transformation lautet:

Abbildung 6 Gleichung der Fisher-Transformation

 Abbildung 5 Fisher-Transformation

Abbildung 6 Fisher-Transformation  

Wie bereits erwähnt ist das Ergebnis der Fisher-Transformation ungefähr die Gauß'sche Wahrscheinlichkeitsdichtefunktion (PDF). Betrachten Sie Abbildung 6.

Wenn die Input-Daten nahe am Mittelwert sind, ist der Gewinn ungefähr einheitlich (siehe Chart |X<0.5|). Wenn der normalisierte Input aber eines der beiden Limits erreicht, wird der Output vergrößert (siehe Chart 0.5<|x|<1). Praktisch kann man an eine "fast schon Gauß'sche" Kurve denken, wenn die meisten Abweichungen auftreten - also genau, was mit der transformierten PDF passiert.  

Wie können wir die Fisher-Transformation beim Handeln anwenden? Aufgrund der Bedingung |x|<1 müssen Preise in diesem Bereich normalisiert werden. Wenn die normalisierten Preise mit der Fisher-Transformation behandelt werden, werden extreme Preisbewegungen relativ selten. Das heißt, dass die Fisher-Transformation diese extremen Preisbewegungen auffängt und es uns erlaubt, diesen Extremen entsprechend zu handeln.


3. Die Fisher-Transformation in MQL5

Der Quellcode des Indikators der Fisher-Transformation wird in Ehlers Buch "Cybernetic Analysis for Stocks and Futures" (Kybernetische Analyse für Wertpapiere und Terminkontrakte) beschrieben.

Er wurde bereits in MQL4 implementiert, und ich habe ihn nach MQL5 konvertiert. Der Indikator verwendet die Medianwerte (H+L)/2 und ich verwende die Funktion iMA(), um Medianwerte aus dem Verlauf zu extrahieren.

Zuerst werden die Preise innerhalb eines Bereiches von 10 Balken normalisiert und die normalisierten Preise werden dann der Fisher-Transformation unterzogen.

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

Beachten Sie, dass klare Signale generiert werden.

Die Signal-Zeile ist einfach der von der Fisher-Transformation veränderte Preis, einen Balken nach hinten versetzt:

Indikator der Fisher-Transformation 

Abbildung 7 Indikator der Fisher-Transformation  

 

4. Die umgekehrte Fisher-Transformation und ihre Anwendung bei Zyklus-Indikatoren

Die Gleichung der umgekehrten Fisher-Transformation erhält man durch das Lösen der Gleichung der Fisher-Transformation, wenn man x statt y setzt:

Abbildung 8 Gleichung der umgekehrten Fisher-Transformation,

Abbildung 6 Die umgekehrte Fisher-Transformation 

Abbildung 8 Die umgekehrte Fisher-Transformation 

Die Transformation-Antwort dieser Funktion ist das Gegenteil der Antwort der Fisher-Transformation.

Für |x|>2 ist der Input komprimiert um die Einheit nicht zu übersteigen (für negative Zahlen -1 und positive +1), und für x|<1 gibt es eine beinahe lineare Beziehung, was heißt, dass der Output mehr oder weniger die selben Eigenschaften hat wie der Input.

Das Ergebnis heißt, wenn die umgekehrte Fisher-Transformation auf ordentlich vorbereitete Input-Daten angewandt wird, hat der Output eine große Chance, -1 oder +1 zu sein. Das macht die umgekehrte Fisher-Transformation eine perfekte Anwendung für oszillierende Indikatoren. Die umgekehrte Fisher-Transformation kann diese verbessern, indem sie genaue Kaufen- oder Verkaufen-Signale gibt.  

 

5. Beispiel der umgekehrten Fisher-Transformation in MQL5

Um die umgekehrte Fisher-Transformation zu bestätigen, habe ich die MQL5-Version von Sylvain Vervoorts Indikator "Smoothed RSI Inverse Fisher Transform" implementiert, die im Magazin "Stocks and Commodities" vom Oktober 2010 vorgestellt wurde. Ich habe ein Handels-Signal-Modul und einen Expert Advisor nach den Vorgaben dieses Indikators gebaut.

Der Indikator der umgekehrten Fisher-Transformation ist bereits für mehrere Handels-Plattformen implementiert und die Quellcodes sind auch auf den Webseiten traders.com und MQL5.com Code Base erhältlich.

Da es keine iRSIOnArray-Funktion in MQL5 gab, habe ich sie zum Indikator-Code hinzugefügt. Der einzige Unterschied zum Original-Indikator ist, dass die Standard-RSI-Periode auf 21 eingestellt ist und die EMA-Periode auf 34, da es so besser für meine Einstellungen funktionierte (EURUSD 1H). Sie können es ruhig auf die Standard-RSI-Periode 4 und EMA-Periode 4 zurücksetzen.

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

 Indikator der umgekehrten Fisher-Transformation

Abbildung 9 Indikator der umgekehrten Fisher-Transformation  

Da ich lediglich Transformationsgleichungen vorgestellt habe, wundern Sie sich vielleicht über die Entstehung der Fisher-Transformation und der umgekehrten Fisher-Transformation.

Als ich Material für den obigen Artikel sammelte, wuchs mein Interesse an der Entstehungsgeschichte der beiden Transformationen, aber ich konnte im Internet nichts darüber finden.

Aber bei der Betrachtung der beiden Transformationen fiel mir einen Ähnlichkeit mit trigonometrischen oder hyperbolischen Funktionen auf (sehen Sie die Ähnlichkeit?). Da diese Funktionen von der Euler'schen Formel abgeleitet werden und mit der Euler'schen Zahl 'e' ausgedrückt werden können, schaute ich mir Mathematikbücher an, um dies zu überprüfen.

Abbildung 9 Sinh-Gleichung,

Abbildung 11 Cosh-Gleichung,

und da wir wissen, dass die Gleichung tanh(x) durch folgendes herbeigeführt werden kann:

Abbildung 12 Tanh-Gleichung,

und... 

Abbildung 12 Atanh-Gleichung 

Ja, das sind genau die selben Gleichungen, die ich oben angegeben habe.  Somit ist das Geheimnis der Fisher-Transformation gelöst! Die Fisher-Transformation ist einfach arctanh(x) und die umgekehrte Fisher-Transformation ist tanh(x)!


6. Modul der Handelssignale

Um die umgekehrte Fisher-Transformation zu verifizieren habe ich ein Modul für Handelssignale gebaut, das auf dem Indikator der umgekehrten Fisher-Transformation basiert.

Ein Handelsmodul auf Grundlage eines benutzerdefinierten Indikators ist sicherlich nützlich. Ich verwende ein Exemplar der CiCustom-Klasse, um den umgekehrten Fisher-Indikator zu halten und überschreibe vier virtuelle Methoden der CExpertSignal-Klasse. CheckOpenLong() und CheckOpenShort() sind für die Generierung von Signalen zuständig, wenn es keine offene Position gibt und CheckReverseLong() und CheckReverseShort() kehren offene Positionen um.  

//+------------------------------------------------------------------+
//|                               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

Um die umgekehrte Fisher-Transformation zu verifizieren, baue ich einen Standard-Expert-Advisor, der das zuvor vorgestellte Handelssignal-Modul verwendet.

Ich verwende auch das Modul "Trailing Stop Loss", das im Artikel "MQL5 Wizard: How to Create a Module of Trailing of Open Positions" (Wie man das Modul "Trailing Open Position" mit dem MQL5-Wizard entwirft) näher beschrieben wird.

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

Ich muss zugeben, dass der Expert Advisor nicht für jedes Asset oder jeden Zeitrahmen rentabel war, aber ich habe ihn verbessert, damit er gute Ergebnisse für den Zeitrahmen EURUSD 1H einbringt.

Ich ermutige Leser dazu, das Signal-Modul und die Einstellung der Indikatoren zu ändern, dann finden Sie vielleicht rentablere Expert Advisor, als die in diesem Artikel vorgestellten.

EA Graph 

Abbildung 10 Expert Advisor der umgekehrten Fisher-Transformation

EA Ergebnis

Abbildung 11 Expert Advisor Bilanzgraph der umgekehrten Fisher-Transformation


Fazit

Ich hoffe, dieser Artikel bietet eine gute Einführung zur Fisher-Transformation und umgekehrten Fisher-Transformation und zeigt eine verständliche Methode, wie man ein Handelssignal-Modul auf Grundlage eines benutzerdefinierten Indikators baut.

Ich habe Sylvain Vervoorts Indikator "Smoothed RSI Inverse Fisher Transform" verwendet, aber eigentlich kann man die umgekehrte Fisher-Transformation auf jeden Oszillator anwenden und Expert Advisor mit Hilfe dieses Artikel bauen.

Ich rate Lesern auch dazu, die Einstellungen zu verbessern, um einen rentablen Expert Advisor zu erstellen auf Grundlage des von mir präsentierten. Ich stelle hier noch externe Links für weitere Vertiefung zur Verfügung.


Literatur

  1. The Fisher Transform
  2. Using the Fisher Transform
  3. The Inverse Fisher Transform
  4. Smoothed RSI Inverse Fisher Transform

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/303

Beigefügte Dateien |
Wie man mit einem UML-Werkzeug einen Expert Advisor entwickelt Wie man mit einem UML-Werkzeug einen Expert Advisor entwickelt
In diesem Artikel wird die Erstellung von Expert Advisors mittels UML (Unified Modeling Language) beschrieben, einer Modellierungssprache, die für die bildliche Modellierung von objektorientierten Softwaresystemen verwendet wird. Der Hauptvorteil dieses Ansatzes ist die Verbildlichung des Modellierungsprozesses. In diesem Artikel wird auch ein Beispiel vorgestellt, das die Modellierung von Struktur und Eigenschaften eines Expert Advisors mit dem Programm "Software Ideas Modeler" zeigt.
Überweisungen und Zahlungsmethoden Überweisungen und Zahlungsmethoden
Die MQL5.community Services bieten sowohl Händlern als auch den Entwicklern von Anwendungen für das MetaTrader-Terminal großartige Möglichkeiten. In diesem Artikel erklären wir, wie Zahlungen für MQL5-Dienstleistungen durchgeführt werden, wie verdientes Geld abgehoben werden kann und wie die Betriebssicherheit gewährleistet wird.
Hinzufügen von neuen UI-Sprachen zur MetaTrader5-Plattform Hinzufügen von neuen UI-Sprachen zur MetaTrader5-Plattform
Die Benutzerschnittstelle der MetaTrader5-Plattform wird in mehrere Sprachen übersetzt. Keine Sorge, wenn Ihre Sprache nicht unter den unterstützten aufscheint. Mit dem kostenlosen Paket "MetaTrader-5-MultiLanguage" von MetaQuotes Software Corp. können Sie ganz einfach eine Übersetzung durchführen. In diesem Artikel werden wir an einigen Bespielen zeigen, wie man eine neue UI-Sprache zur MetaTrader5-Plattform hinzufügen kann.
Die Verwendung von WinInet in MQL5   Teil 2:  POST-Anfragen und -Dateien Die Verwendung von WinInet in MQL5 Teil 2: POST-Anfragen und -Dateien
In diesem Artikel werden wir uns weiterhin mit den Grundlagen von internetbasierten HTTP-Anfragen und dem Informationsaustausch mit Servern befassen. Es werden neue Funktionen der CMqINet-Klasse, Methoden der Informationsübertragung mit Formularen, das Senden von Dateien mit POST-Anfragen sowie Autorisierungen auf Webseiten mit Ihrem Login unter Verwendung von Cookies behandelt.