
Théorie et Implémentation des Indicateurs Adaptatifs Avancés dans MQL5
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.
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 :
et l'équation du composant InPhase est retardée de trois barres par le prix :
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 et phase de la barre précédente est
. Utilisation de l'identité trigonométrique :
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.
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.
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 , 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.
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 :
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 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.
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.
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 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 :
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





- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation