English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Théorie et Implémentation des Indicateurs Adaptatifs Avancés dans MQL5

Théorie et Implémentation des Indicateurs Adaptatifs Avancés dans MQL5

MetaTrader 5Exemples | 12 janvier 2022, 14:47
247 0
investeo
investeo

Introduction

Cet article est axé sur deux excellents livres de John F. Ehlers : "Rocket Science for Traders" et "Сybernetic Analysis for Stock and Futures". L'approche inhabituelle de l'analyse de marché à l'aide de méthodes de traitement du signal numérique et l'adoption de nombres complexes pour la reconnaissance du cycle de marché m'ont permis d'approfondir ce sujet et d'implémenter par la suite dans MQL5 trois indicateurs adaptatifs présentés par J.F.Ehlers.

Cet article décrira la théorie de base derrière les indicateurs adaptatifs et leur implémentation MQL5. Les indicateurs adaptatifs seront comparés à leurs homologues non adaptatifs.


Nombres complexes et phaseurs pour mesurer les cycles de marché

La notion de théorie des nombres complexes peuvent être assez perplexes pour les lecteurs sans connaissances en ingénierie et par conséquent, je recommande deplonger dans la théoriesur wiki et regarder le tutoriel  sur les opération sur les nombres complexes avant de lire cet article. 

Phasor

Le phaseur ou vecteur de phase est un vecteur qui indique l'amplitude et la phase d'un cycle. Selon laformule de Euler'sune onde sinusoïdale peut être représentée comme somme de composants de deux nombres complexes. Veuillez observer le phaseur rotatif représentant un cycle sinusoïdal ci-dessous.

Phaseur 


En voyant cette animation pour la première fois, vous êtes un peu confus quant à la façon d’interpréter correctement le rapport du phaseur envers un cycle. Pour comprendre cela, vous devez changer d'avis pour reconnaître un cycle non pas comme une forme d'onde habituelle visible sur la partie gauche de l'animation, mais comme le phaseur rotatif sur la droite.

Au début, cela peut être difficile à imaginer, mais j'ai trouvé un moyen d'y penser de cette façon : la rotation complète d'un phaseur est de 360 degrés ou en radian, c'est la même chose pour un cycle complet. L'angle actuel d'un phaseur indique dans quelle partie du cycle (phase) nous nous trouvons. L'axe Y représente l'amplitude d'un cycle dans une phase donnée.

Le phaseur peut être décomposé en deux composants : Composant InPhase (cosinus) et composant en quadrature (sinus). Une explication détaillée de la dérivation de ces composants est disponible dans le chapitre 6 "Hilbert Transforms" du livre "Rocket Science for Traders". Si quelqu'un est intéressé, veuillez suivre attentivement ce chapitre.

Pour l'instant, vous devez seulement vous concentrer sur le fait que pour le calcul des indicateurs adaptatifs, nous devons convertir le signal analytique (forme d'onde) en un signal complexe constitué de deux composants. Comment y parvenir ? Ai-je mentionné Hilbert Transform? Oui en effet. Hilbert Transformest en mesure de faire cela.


Mesure de la période de cycle

Afin de rendre Hilbert Transform pratique pour les traders, John Ehlers dans son livre a tronqué la série Hilbert Transform en quatre éléments.

L'équation du composant en quadrature est :

Équation du composant de quadrature  

et l'équation du composant InPhase est retardée de trois barres par le prix :

Composant de l’équation InPhase 

Après avoir calculé les composants InPhase et Quadrature, il est possible de dériver un calcul de phase différentiel à partir de l'angle de phase mesuré pour la barre actuelle et de l'angle de phase mesuré il y a une barre. Phase de la barre actuelle est  Phase de la barre actuelle et phase de la barre précédente est  barre précédente Arctan. Utilisation de l'identité trigonométrique :

Arctan deltaphase 

nous obtenons une équation pour la phase différentielle appelée DeltaPhase.

M. Ehlers a mis des contraintes supplémentaires sur la variable DeltaPhase : le résultat ne peut pas être négatif et DeltaPhase est limité à <0,1, 1,1> radians (soit un cycle entre 6 et 63 barres). Il est apparu que DeltaPhase mesuré sur des données réelles est très bruyant, il a donc besoin d'être lissé.

La meilleure méthode de lissage sur les données bruyantes est le filtre médian, donc une médiane de cinq échantillons de DeltaPhase forme la variable MedianDelta. MedianDelta divisé par est utilisé pour calculer le Cycle Dominant, le cycle de marché que nous recherchons.

Au cours des tests de développement, il s'est avéré qu'il y a un biais d'environ 0,5 dans la mesure qui doit être supprimé et un terme de compensation pour supprimer ce biais a été ajouté. Enfin, le Cycle Dominant est lissé deux fois par EMA avec des valeurs alpha respectivement égales à 0,33 et 0,15,. Je recommande vivement de lire le livre pour voir la robustesse de l'algorithme appliqué à une onde sinusoïdale dont la période de cycle est passée progressivement de 6 à 40.

Puisque vous êtes doté de connaissances théoriques, nous sommes prêts à implémenter l'indicateur CyclePeriod dans MQL5.


Indicateur de Période de Cycle

L'indicateur est composé de deux lignes : une ligne de cycle indiquant la période de cycle et une ligne de déclenchement, qui est essentiellement une ligne de cycle retardée d'une barre. Si vous suivez la description dans la section "Mesure de la période du cycle" et le code source dans la fonction OnCalculate(), vous pourrez facilement corréler les lignes chargées de mesurer la période de cycle.

//+------------------------------------------------------------------+
//|                                                  CyclePeriod.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "CyclePeriod indicator - described by John F. Ehlers"
#property description "in \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];
//double Price[];
double Q1[]; // Quadrature component
double I1[]; // InPhase component
double DeltaPhase[];
double InstPeriod[];
double CyclePeriod[];


input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(CyclePeriod,true);
   ArraySetAsSeries(Trigger,true); 
   ArraySetAsSeries(Smooth,true);
   //ArraySetAsSeries(Price,true);
   
   SetIndexBuffer(0,CyclePeriod,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);
   
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   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[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double DC, MedianDelta;

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- last counted bar will be recounted
      int nLimit=rates_total-prev_calculated-1; // start index for calculations

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);
      
      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      //ArrayResize(Price,Bars(_Symbol,_Period));
      ArrayResize(CyclePeriod,Bars(_Symbol,_Period));
      ArrayResize(InstPeriod,Bars(_Symbol,_Period));
      ArrayResize(Q1,Bars(_Symbol,_Period));
      ArrayResize(I1,Bars(_Symbol,_Period));
      ArrayResize(DeltaPhase,Bars(_Symbol,_Period));
      
      if (nLimit>rates_total-7) // adjust for last bars
         nLimit=rates_total-7;   
      
      for(i=nLimit;i>=0 && !IsStopped();i--)   
      {
         Smooth[i] = (Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;
   
         if (i<rates_total-7)
         {
            Cycle[i] = (1.0-0.5*InpAlpha) * (1.0-0.5*InpAlpha) * (Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                      +2.0*(1.0-InpAlpha)*Cycle[i+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[i+2];
                   
         } else         
         {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
         }
         
         Q1[i] = (0.0962*Cycle[i]+0.5769*Cycle[i+2]-0.5769*Cycle[i+4]-0.0962*Cycle[i+6])*(0.5+0.08*InstPeriod[i+1]);
         I1[i] = Cycle[i+3];
         
         if (Q1[i]!=0.0 && Q1[i+1]!=0.0) 
            DeltaPhase[i] = (I1[i]/Q1[i]-I1[i+1]/Q1[i+1])/(1.0+I1[i]*I1[i+1]/(Q1[i]*Q1[i+1]));
         if (DeltaPhase[i] < 0.1)
            DeltaPhase[i] = 0.1;
         if (DeltaPhase[i] > 0.9)
            DeltaPhase[i] = 0.9;
        
         MedianDelta = Median(DeltaPhase, i, 5);
         
         if (MedianDelta == 0.0)
            DC = 15.0;
         else
            DC = (6.28318/MedianDelta) + 0.5;
        
         InstPeriod[i] = 0.33 * DC + 0.67 * InstPeriod[i+1];
         CyclePeriod[i] = 0.15 * InstPeriod[i] + 0.85 * CyclePeriod[i+1];
         Trigger[i] = CyclePeriod[i+1];
      }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

double Median(double& arr[], int idx, int m_len)
{
   double MedianArr[];
   int copied;
   double result = 0.0;
   
   ArraySetAsSeries(MedianArr, true);
   ArrayResize(MedianArr, m_len);
   
   copied = ArrayCopy(MedianArr, arr, 0, idx, m_len);
   if (copied == m_len)
   {
      ArraySort(MedianArr);
      if (m_len %2 == 0) 
            result = (MedianArr[m_len/2] + MedianArr[(m_len/2)+1])/2.0;
      else
            result = MedianArr[m_len / 2];
      
   }
   else Print(__FILE__+__FUNCTION__+"median error - wrong number of elements copied."); 
   return result; 
}

Nous pouvons le tester en l'attachant à n'importe quel graphique - cela fonctionnera pour n'importe quelle sécurité et pour n'importe quel laps de temps.

Veuillez voir la capture d'écran ci-dessous.

Indicateur de CyclePeriod  

Avec cet indicateur dans notre espace de travail, nous sommes en mesure d’implémenter une nouvelle génération d'indicateurs adaptatifs - des indicateurs qui s'adaptent à une période de cycle actuelle du marché.


Indicateur du Cyber Cycle

L’indicateur de Cyber Cycle est un filtre à passe-haut pris depuis "Сybernetic analysis for stocks and futures". Ce filtre ne laisse que le composant de mode cycle des séries chronologiques.

Accessoirement les composants de cycle à deux et trois barres sont extraits depuis le résultat en le lissant à l’aide d’unfiltre à réponse impulsionnelle finifiltre à passe-bas.

L'indicateur code MQL5 pour cet indicateur et d'autres dans l'article adapté du langage EFL (Tradestation) décrit dans le livre. 

//+------------------------------------------------------------------+
//|                                                   CyberCycle.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "CyberCycle indicator - described by John F. Ehlers"
#property description "in \"Cybernetic Analysis for Stocks and Futures\""
#property description "This indicator is available for free download."

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   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[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- last counted bar will be recounted
      int nLimit=rates_total-prev_calculated-1; // start index for calculations

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      if(nLimit>rates_total-4) // adjust for last bars
         nLimit=rates_total-4;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Smooth[i]=(Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;

         if(i<rates_total-5)
           {
            Cycle[i]=(1.0-0.5*InpAlpha) *(1.0-0.5*InpAlpha) *(Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                     +2.0*(1.0-InpAlpha)*Cycle[i+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[i+2];
           }
         else
           {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
           }

         //Print(__FILE__+__FUNCTION__+" received values: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Une capture d'écran de l'indicateur est collée ci-dessous.

Comme vous pouvez le constater, tous les indicateurs de cet article afficheront une apparence similaire, mais ils implémentent des algorithmes très différents.

Indicateur de CyclePeriod  

La méthode de trading originale pour cet indicateur est simple : achetez lorsque la ligne de cycle passe au-dessus de la ligne de déclenchement. Vendez lorsque la ligne de cycle passe en-dessous de la ligne de déclenchement. Vous souhaitez peut-être et êtes encouragé à mettre en œuvre votre propre stratégie et un module de signaux de trading utilisant cet indicateur.


Indicateur de Cyber Cycle Adaptatif.

L'essence de cet article est de présenter comment rendre les indicateurs adaptatifs, c'est-à-dire comment les calculer avec des entrées de période de cycle dynamique au lieu d'un réglage statique. Pour y parvenir, nous devons nous connecter à l'indicateur CyclePeriod pour lire la période actuelle et utiliser plus tard cette lecture dans la fonctionOnCalculate()

Dans un premier temps, nous devons obtenir la poignée de l'indicateur :

hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("CyclePeriod indicator not available!");
      return(-1);
     }

 puis lisez-le dans la fonction OnCalculate() :

int copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);
    if(copied<=0)
      {
       Print("FAILURE: Could not get values from CyclePeriod indicator.");
       return -1;
      }
alpha1 = 2.0/(CyclePeriod[0]+1.0);

L'alpha mobile exponentiel est lié à la longueur d'une moyenne mobile simple par l'équation alpha, dans l'indicateur de Cyber Cycle adaptatif, M. Ehlers a utilisé la période du Cycle Dominant comme longueur dans le calcul du coefficient alpha1.

Le code source complet est disponible ci-dessous :

//+------------------------------------------------------------------+
//|                                           AdaptiveCyberCycle.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Adaptive CyberCycle indicator - described by John F. Ehlers"
#property description "in \"Cybernetic Analysis for Stocks and Futures\""
#property description "This indicator is available for free download."

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

int hCyclePeriod;
 
input double InpAlpha=0.07; // alpha for Cycle Period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("CyclePeriod indicator not available!");
      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[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double CyclePeriod[1],alpha1;

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- last counted bar will be recounted
      int nLimit=rates_total-prev_calculated-1; // start index for calculations

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      if(nLimit>rates_total-4) // adjust for last bars
         nLimit=rates_total-4;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Smooth[i]=(Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;
         int copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

         if(copied<=0)
           {
            Print("FAILURE: Could not get values from CyclePeriod indicator.");
            return -1;
           }
         alpha1 = 2.0/(CyclePeriod[0]+1.0);
         //Print(alpha1);
         //Print(CyclePeriod[0]);
         if(i>=0)
           {
            Cycle[i]=(1.0-0.5*alpha1) *(1.0-0.5*alpha1) *(Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                     +2.0*(1.0-alpha1)*Cycle[i+1]-(1.0-alpha1)*(1.0-alpha1)*Cycle[i+2];

            //Print("Smooth["+IntegerToString(i)+"]="+DoubleToString(Smooth[i])+" Cycle["+IntegerToString(i)+"]="+DoubleToString(Cycle[i]));
           }
         else
           {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
           }

         //Print(__FILE__+__FUNCTION__+" received values: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

 Veuillez voir l'indicateur sur la capture d'écran ci-jointe.

Indicateur du CyberCycle adaptatif 

Notre premier indicateur adaptatif est prêt. Selon le livre, il devrait être plus réactif que la version non adaptative.

Les signaux d'achat et de vente devraient souvent intervenir une barre plus tôt que pour la version non adaptative.

Nous pouvons procéder avec deux autres exemples d'indicateurs, cela devrait vous suffire pour comprendre le schéma de création d'indicateurs adaptatifs.


Indicateur de Centre de Gravité

Lorsque nous parlons du centre de gravité d'un objet physique, nous entendons son point d'équilibre. L'idée d'introduire ce concept dans le trading est venue de l'observation de la relation entre les décalages de divers filtres et les coefficients des filtres.

Pour SMA - Simple Moving Average, tous les coefficients sont égaux, le centre de gravité est au milieu.

Pour WMA - Weighted Moving Average, les derniers prix sont plus importants que les plus anciens. Pour être précis, les coefficients de WMA décrivent le contour du triangle. Le centre de gravité du triangle est dans un tiers de la longueur de la base du triangle. L'équation plus générique dérivée pour calculer le centre de gravité sur une fenêtre d'observation donnée est la suivante :

CenterOfGravity 

La position du point d'équilibre est la somme du produit de la position dans la fenêtre fois le prix de cette position (+1 dans l'équation a été introduit car nous comptons de 0 à N et non de 1 à N) divisé par la somme de prix dans la fenêtre.

La principale caractéristique du CG est qu'il diminue et augmente avec les fluctuations de prix et qu'il s'agit essentiellement d'un oscillateur à décalage nul.

Veuillez trouver le code source ci-dessous :

//+------------------------------------------------------------------+
//|                                              CenterOfGravity.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "CG indicator - described by John F. Ehlers"
#property description "in \"Cybernetic Analysis for Stocks and Futures\""
#property description "This indicator is available for free download."

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

input double InpAlpha=0.07; // alpha
input int InpCGLength=10; //CG window size

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);
   
   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   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[])
  {
//---   
   long tickCnt[1];
   int i;
   double Num, Denom; // Numerator and Denominator for CG
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- last counted bar will be recounted
      int nLimit=rates_total-prev_calculated-1; // start index for calculations

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));

      if(nLimit>rates_total-InpCGLength) // adjust for last bars
         nLimit=rates_total-InpCGLength;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Num = 0.0;
         Denom = 0.0;
         for (int count=0; count<InpCGLength; count++)
            {
               Num += (1.0+count)*Price(i+count);
               Denom += Price(i+count);
            }
         if (Denom != 0.0)
            Cycle[i] = -Num/Denom+(InpCGLength+1.0)/2.0;
         else
            Cycle[i] = 0.0;
         
         //Print(__FILE__+__FUNCTION__+" received values: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+ 

Une capture d'écran ci-dessous. Veuillez noter le petit décalage.

  Indicateur du Centre de Gravité


Indicateur de Centre de Gravité Adaptatif

L'oscillateur CG trouve le centre de gravité sur un intervalle de temps à longueur fixe. L'oscillateur CG adaptatif utilise la moitié de la période du Cycle Dominant mesurée comme longueur de fenêtre dynamique. Afin d'extraire la moitié de la période du Cycle Dominant mesurée, le code suivant a été utilisé. 

copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

if(copied<=0)
  {
   Print("FAILURE: Could not get values from CyclePeriod indicator.");
   return -1;
  }
CG_len = floor(CyclePeriod[0]/2.0);

Veuillez trouver le code source complet de l'indicateur ci-dessous et le comparer à sa version non adaptative ainsi qu'à l'indicateur Adaptive Cyber Cycle pour les similitudes.

//+------------------------------------------------------------------+
//|                                      AdaptiveCenterOfGravity.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Adaptive CG indicator - described by John F. Ehlers"
#property description "in \"Cybernetic Analysis for Stocks and Futures\""
#property description "This indicator is available for free download."

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

int hCyclePeriod;

input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("CyclePeriod indicator not available!");
      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[])
  {
//---   
   long tickCnt[1];
   int i, copied;
   double Num,Denom; // Numerator and Denominator for CG
   double CG_len;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double CyclePeriod[1];

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- last counted bar will be recounted
      int nLimit=rates_total-prev_calculated-1; // start index for calculations

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      copied=CopyBuffer(hCyclePeriod,0,0,1,CyclePeriod);

      if(copied<=0)
        {
         Print("FAILURE: Could not get values from CyclePeriod indicator.");
         return -1;
        }

      if(nLimit>rates_total-int(CyclePeriod[0])-2) // adjust for last bars
         nLimit=rates_total-int(CyclePeriod[0])-2;


      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

         if(copied<=0)
           {
            Print("FAILURE: Could not get values from CyclePeriod indicator.");
            return -1;
           }
         CG_len = floor(CyclePeriod[0]/2.0);
         //Print("CG_len="+DoubleToString(CG_len));
         
         Num=0.0;
         Denom=0.0;
         for(int count=0; count<int(CG_len); count++)
           {
            Num+=(1.0+count)*Price(i+count);
            Denom+=Price(i+count);
           }
         if(Denom!=0.0)
            Cycle[i]=-Num/Denom+(CG_len+1.0)/2.0;
         else
            Cycle[i]=0.0;

         //Print(__FILE__+__FUNCTION__+" received values: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Veuillez observer la capture d'écran de l'indicateur AdaptiveCG collée ci-dessous. 

 Capture d’écran du Centre de Gravité Adaptatif


Indicateur RVI

.RVI indiqueRelative Vigor Index. La théorie de base derrière cet indicateur est que les prix ont tendance à avoir un prix de clôture supérieur au prix d'ouverture sur les marchés haussiers et un prix de clôture inférieur au prix d'ouverture sur les marchés baissiers.

La vigueur du mouvement est mesurée par la différence entre le prix de clôture et le prix d'ouverture par rapport à la fourchette quotidienne de trading.

RVI 

Il s'agit d'un indicateur bien connu pour de nombreux utilisateurs de MetaTrader car il est maintenant intégré à l'installation de MetaTrader 5.

Je colle toujours le code source pour référence :

//+------------------------------------------------------------------+
//|                                                          RVI.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "2009, MetaQuotes Software Corp."
#property link        "https://www.mql5.com"
#property description "Relative Vigor Index"
//--- indicator settings
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "RVI"
#property indicator_label2  "Signal"
//--- input parameters
input int InpRVIPeriod=10; // Period
//--- indicator buffers
double    ExtRVIBuffer[];
double    ExtSignalBuffer[];
//---
#define TRIANGLE_PERIOD  3
#define AVERAGE_PERIOD   (TRIANGLE_PERIOD*2)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtRVIBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ExtSignalBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,3);
//--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+TRIANGLE_PERIOD);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+AVERAGE_PERIOD);
//--- name for DataWindow and indicator subwindow label
   IndicatorSetString(INDICATOR_SHORTNAME,"RVI("+string(InpRVIPeriod)+")");
   PlotIndexSetString(0,PLOT_LABEL,"RVI("+string(InpRVIPeriod)+")");
   PlotIndexSetString(1,PLOT_LABEL,"Signal("+string(InpRVIPeriod)+")");
//--- initialization done
  }
//+------------------------------------------------------------------+
//| Relative Vigor Index                                             |
//+------------------------------------------------------------------+
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 &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   int    i,j,nLimit;
   double dValueUp,dValueDown,dNum,dDeNum;
//--- check for bars count
   if(rates_total<=InpRVIPeriod+AVERAGE_PERIOD+2) return(0); // exit with zero result
//--- check for possible errors
   if(prev_calculated<0) return(0); // exit with zero result
//--- last counted bar will be recounted
   nLimit=InpRVIPeriod+2;
   if(prev_calculated>InpRVIPeriod+TRIANGLE_PERIOD+2)
      nLimit=prev_calculated-1;
//--- set empty value for uncalculated bars
   if(prev_calculated==0)
     {
      for(i=0;i<InpRVIPeriod+TRIANGLE_PERIOD;i++) ExtRVIBuffer[i]=0.0;
      for(i=0;i<InpRVIPeriod+AVERAGE_PERIOD;i++)  ExtSignalBuffer[i]=0.0;
     }
//--- RVI counted in the 1-st buffer
   for(i=nLimit;i<rates_total && !IsStopped();i++)
     {
      dNum=0.0;
      dDeNum=0.0;
      for(j=i;j>i-InpRVIPeriod;j--)
        {
         dValueUp=Close[j]-Open[j]+2*(Close[j-1]-Open[j-1])+2*(Close[j-2]-Open[j-2])+Close[j-3]-Open[j-3];
         dValueDown=High[j]-Low[j]+2*(High[j-1]-Low[j-1])+2*(High[j-2]-Low[j-2])+High[j-3]-Low[j-3];
         dNum+=dValueUp;
         dDeNum+=dValueDown;
        }
      if(dDeNum!=0.0)
         ExtRVIBuffer[i]=dNum/dDeNum;
      else
         ExtRVIBuffer[i]=dNum;
     }
//--- signal line counted in the 2-nd buffer
   nLimit=InpRVIPeriod+TRIANGLE_PERIOD+2;
   if(prev_calculated>InpRVIPeriod+AVERAGE_PERIOD+2)
      nLimit=prev_calculated-1;
   for(i=nLimit;i<rates_total && !IsStopped();i++) 
      ExtSignalBuffer[i]=(ExtRVIBuffer[i]+2*ExtRVIBuffer[i-1]+2*ExtRVIBuffer[i-2]+ExtRVIBuffer[i-3])/AVERAGE_PERIOD;

//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+

Une capture d'écran de l'indicateur RVI standard avec une période définie par défaut sur 10 est collée ci-dessous.

Indicateur RVI 

 

Indicateur RVI adaptatif

Comme pour les deux indicateurs adaptatifs précédents, nous devons extraire la mesure du Cycle Dominant de l'indicateur CyclePeriod et l'appliquer à la période RVI. La variable « Longueur » est calculée comme une moyenne mobile pondérée à quatre barres de la période :

copied=CopyBuffer(hCyclePeriod,0,0,4,CyclePeriod);

if(copied<=0)
  {
   Print("FAILURE: Could not get values from CyclePeriod indicator.");
   return -1;
  }
AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));

Veuillez trouver ci-dessous le code source complet de l'indicateur RVI adaptatif. 

//+------------------------------------------------------------------+
//|                                                 Adaptive RVI.mq5 |
//|                        Based on RVI by MetaQuotes Software Corp. |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "2009, MetaQuotes Software Corp."
#property copyright   "2011, Adaptive version Investeo.pl"
#property link        "https://www.mql5.com"
#property description "Adaptive Relative Vigor Index"
//--- indicator settings
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "AdaptiveRVI"
#property indicator_label2  "Signal"

#define Price(i) ((high[i]+low[i])/2.0)

//--- input parameters
input int InpRVIPeriod=10; // Initial RVI Period
//--- indicator buffers
double    ExtRVIBuffer[];
double    ExtSignalBuffer[];
//---
int hCyclePeriod; 
input double InpAlpha=0.07; // alpha for Cycle Period
int AdaptiveRVIPeriod;

#define TRIANGLE_PERIOD  3
#define AVERAGE_PERIOD   (TRIANGLE_PERIOD*2)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtRVIBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ExtSignalBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,3);
   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("CyclePeriod indicator not available!");
      return(-1);
     }
   
//--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+TRIANGLE_PERIOD);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+AVERAGE_PERIOD);
//--- name for DataWindow and indicator subwindow label
   IndicatorSetString(INDICATOR_SHORTNAME,"AdaptiveRVI");
   PlotIndexSetString(0,PLOT_LABEL,"AdaptiveRVI");
   PlotIndexSetString(1,PLOT_LABEL,"Signal");
//--- initialization done
  return 0;
  }
//+------------------------------------------------------------------+
//| Relative Vigor Index                                             |
//+------------------------------------------------------------------+
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 &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   int    i,j,nLimit;
   double dValueUp,dValueDown,dNum,dDeNum;
   double CyclePeriod[4];
   int copied;
   
   copied=CopyBuffer(hCyclePeriod,0,0,4,CyclePeriod);

         if(copied<=0)
           {
            Print("FAILURE: Could not get values from CyclePeriod indicator.");
            return -1;
           }
   AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));
//--- check for bars count
   if(rates_total<=AdaptiveRVIPeriod+AVERAGE_PERIOD+2) return(0); // exit with zero result
//--- check for possible errors
   if(prev_calculated<0) return(0); // exit with zero result
//--- last counted bar will be recounted
   nLimit=AdaptiveRVIPeriod+2;
   if(prev_calculated>AdaptiveRVIPeriod+TRIANGLE_PERIOD+2)
      nLimit=prev_calculated-1;
//--- set empty value for uncalculated bars
   if(prev_calculated==0)
     {
      for(i=0;i<AdaptiveRVIPeriod+TRIANGLE_PERIOD;i++) ExtRVIBuffer[i]=0.0;
      for(i=0;i<AdaptiveRVIPeriod+AVERAGE_PERIOD;i++)  ExtSignalBuffer[i]=0.0;
     }
//--- RVI counted in the 1-st buffer
   for(i=nLimit;i<rates_total && !IsStopped();i++)
     {
      copied=CopyBuffer(hCyclePeriod,0,rates_total-i-1,4,CyclePeriod);

         if(copied<=0)
           {
            Print("FAILURE: Could not get values from CyclePeriod indicator.");
            return -1;
           }
      AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));
      dNum=0.0;
      dDeNum=0.0;
      for(j=i;j>MathMax(i-AdaptiveRVIPeriod, 3);j--)
        {
         //Print("rates_total="+IntegerToString(rates_total)+" nLimit="+IntegerToString(nLimit)+
         //      " AdaptiveRVIPeriod="+IntegerToString(AdaptiveRVIPeriod)+" j="+IntegerToString(j));
         dValueUp=Close[j]-Open[j]+2*(Close[j-1]-Open[j-1])+2*(Close[j-2]-Open[j-2])+Close[j-3]-Open[j-3];
         dValueDown=High[j]-Low[j]+2*(High[j-1]-Low[j-1])+2*(High[j-2]-Low[j-2])+High[j-3]-Low[j-3];
         dNum+=dValueUp;
         dDeNum+=dValueDown;
        }
      if(dDeNum!=0.0)
         ExtRVIBuffer[i]=dNum/dDeNum;
      else
         ExtRVIBuffer[i]=dNum;
     }
//--- signal line counted in the 2-nd buffer
   nLimit=AdaptiveRVIPeriod+TRIANGLE_PERIOD+2;
   if(prev_calculated>AdaptiveRVIPeriod+AVERAGE_PERIOD+2)
      nLimit=prev_calculated-1;
   for(i=nLimit;i<rates_total && !IsStopped();i++)
    ExtSignalBuffer[i]=(ExtRVIBuffer[i]+2*ExtRVIBuffer[i-1]+2*ExtRVIBuffer[i-2]+ExtRVIBuffer[i-3])/AVERAGE_PERIOD;

//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+ 

Une capture d'écran de l'indicateur RVI adaptatif avec une longueur de fenêtre dynamique :

Indicateur RVI adaptatif 


Conclusion

Cet article a présenté le comportement de trois indicateurs techniques adaptatifs et l'implémentation de MQL5.

Le mécanisme d’implémentation des indicateurs adaptatifs doit être clairement compréhensible après la lecture de l'article. Tous les indicateurs décrits sont disponibles en pièces jointes.

L'auteur encourage à expérimenter et à créer d'autres indicateurs adaptatifs à partir de ceux déjà disponibles.


Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/288

Analyse des Principales Caractéristiques des Séries Chronologiques Analyse des Principales Caractéristiques des Séries Chronologiques
Cet article présente une classe conçue pour donner une estimation préliminaire rapide des caractéristiques de diverses séries chronologiques. Au fur et à mesure que cela se produit, les paramètres statistiques et la fonction d'auto-corrélation sont estimés, une estimation spectrale des séries chronologiques est effectuée et un histogramme est créé.
Assistant MQL5 pour les Nuls Assistant MQL5 pour les Nuls
Début 2011, nous avons publié la première version de l'assistant MQL5. Cette nouvelle application fournit un outil simple et pratique pour générer automatiquement des robots de trading. Tout utilisateur de MetaTrader 5 peut créer un Expert Advisor personnalisé sans même savoir comment programmer en MQL5.
Prévision de Séries Chronologiques à l'Aide du Lissage Exponentiel Prévision de Séries Chronologiques à l'Aide du Lissage Exponentiel
L'article familiarise le lecteur avec les modèles de lissage exponentiel utilisés dans la prévision à court terme des séries chronologiques. De plus, il aborde les questions liées à l'optimisation et à l'estimation des résultats prévisionnels et fournit quelques exemples de scripts et d'indicateurs. Cet article sera utile pour une première prise de connaissance des principes de prévision à partir de modèles de lissage exponentiel.
Création de Critères Personnalisés d’Optimisation des Expert Advisors Création de Critères Personnalisés d’Optimisation des Expert Advisors
Le terminal client MetaTrader 5 offre un large éventail de possibilités d’optimisation des paramètres de l’Expert Advisor. En plus des critères d’optimisation compris dans le testeur de stratégie, les développeurs sont offerts une opportunité de créer leurs propres critères. Cela conduit à un nombre presque illimité de possibilités de test et d’optimisation des Expert Advisors. L’article décrit des méthodes pratiques d’établir de tels critères - à la fois complexes et simples.