English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Indicateur pour le graphique en Points et Figures

Indicateur pour le graphique en Points et Figures

MetaTrader 5Exemples | 13 janvier 2022, 10:47
275 0
Dmitriy Zabudskiy
Dmitriy Zabudskiy

Introduction

hIl existe de nombreux types de graphiques qui fournissent des informations sur la situation actuelle du marché. Beaucoup d’entre eux, tels que Le graphique en points et figures, sont l’héritage du passé lointain.

Ce type de graphique est connu depuis la fin du XIXe siècle. Il a été mentionné pour la première fois par Charles Dow dans son éditorial du Wall Street Journal écrit le 20 juillet 1901 qui l’a qualifié de méthode "book". Et bien que Dow ait fait référence à la méthode "book" dès 1886, il a été le premier à en définir officiellement l’utilisation à ce jour.

Malgré le fait que Dow n’ait décrit cette méthode que dans des éditoriaux, vous pouvez maintenant trouver beaucoup de livres qui donnent les détails de cette méthode. L’un des livres que je recommanderais aux traders débutants est un livre de Thomas J. Dorsey intitulé "Le graphique en Points et Figures : L'application indispensable pour prévoir et suivre le marché".

 

Description

Le graphique en Points et Figures est un ensemble de colonnes verticales : les colonnes de X sont des prix en hausse et les colonnes de O sont des prix en baisse. Il est unique en ce qu’il est tracé en fonction de l’action des prix, et non du temps. Ainsi, après avoir supprimé une valeur des données graphiques (temps), nous obtenons des graphiques avec des lignes de tendance tracées à un angle de 45 degrés.

Les graphiques en Points et Figures sont tracés à l’aide de deux valeurs prédéfinies :

  • La taille de la boîte (Box Size) est le montant du mouvement de prix requis pour ajouter un X ou un O (à l’origine, la valeur était exprimée comme le montant de dollars par action, mais au fil du temps, elle s’est développée en points, ce que nous utiliserons dans notre indicateur).
  • le montant d’inversion (Reversal Amount) est le montant de l’inversion de prix exprimé en unités de taille de boîte nécessaire pour changer les colonnes de X à O ou vice versa (par exemple, un montant d’inversion de 3 et une taille de boîte de 10 points correspondront à 30 points).

Nous sélectionnons donc un point de départ et mettons un X pour une augmentation de prix ou un O pour une baisse, à condition que le prix ait changé de la valeur égale à la taille de la boîte multipliée par le montant d’inversion. De plus, si le prix continue d’évoluer dans la même direction en changeant selon la taille de la boîte, nous ajoutons un X en haut de la colonne du X pour une hausse ou un O en bas de la colonne du O pour une diminution, respectivement. Si le prix a évolué dans la direction opposée par la valeur de la taille de la boîte multipliée par le montant d’inversion, nous mettons un X pour une hausse du prix ou un O pour une baisse du prix, commençant ainsi une nouvelle colonne de X ou une nouvelle colonne de O, respectivement.

Pour plus de commodité, le graphique en Points et Figures est généralement tracé sur du papier à damier. Pour une meilleure compréhension, passons en revue un petit exemple de graphique en Points et Figures. Supposons que nous ayons les données suivantes :

Date Prix élevé Prix bas
07.03.2013 12:00 - 07.03.2013 20:00 1.3117 1.2989
07.03.2013 20:00 - 08.03.2013 04:00 1.3118 1.3093
08.03.2013 04:00 - 08.03.2013 12:00 1.3101 1.3080
08.03.2013 12:00 - 08.03.2013 20:00 1.3134 1.2955

Nous allons tracer le graphique en Points et Figures, étant donné que la taille de la boîte est égale à 10 et le montant d’inversion est égal à 3 :

  • Au début, nous voyons une augmentation du prix de 128 points, de 1,2989 à 1,3117, donc nous tirons 12 X.
  • Le prix chute alors de 1,3118 à 1,3093 de 25 points, ce qui n’est pas suffisant pour le renversement, nous en laissons donc là.
  • De plus, nous pouvons voir que le prix continue de baisser à 1,3080. Compte tenu de la valeur précédente de 1,3118, elle a maintenant changé de 38 points, nous pouvons donc commencer une nouvelle colonne en ajoutant deux O (bien que le mouvement de prix ait dépassé la valeur de trois tailles de boîte, nous n’avons mis que deux O car la colonne suivante de O commence toujours une taille de boîte plus bas).
  • Le prix monte ensuite de 1,3080 à 1,3134 de 54 points, puis tombe à 1,2955, soit 179 points. Ainsi, la colonne suivante se compose de quatre X, suivie d’une colonne de O composée de 16 O.

Voyons-le illustré ci-dessous :

Fig. 1. Graphique en chandelier japonais (à gauche) et graphique en Points et Figures (à droite).

Fig. 1. Graphique en chandelier japonais (à gauche) et graphique en Points et Figures (à droite).

L’exemple ci-dessus de graphique en Points et Figures est très approximatif et est fourni ici pour aider les débutants à mieux comprendre le concept.

 

Principe de graphique

Il existe plusieurs techniques de graphique en Points et Figures, l’une d’entre elles étant déjà décrite ci-dessus. Ces techniques de graphique diffèrent par les données qu’elles utilisent. Par exemple, nous pouvons utiliser des données quotidiennes sans tenir compte des mouvements intrajournaliers, obtenant ainsi un graphique approximatif. Ou nous pouvons considérer les données de mouvement des prix intrajournaliers afin d’obtenir un graphique plus détaillé et plus lisse.

Pour obtenir un graphique en Points et Figures plus fluide et plus précis, il a été décidé d’utiliser les données de la minute pour les calculs et les graphiques, car le mouvement des prix sur une minute n’est pas très significatif et peut généralement atteindre six points, deux ou trois points n’étant pas rares. Ainsi, nous utiliserons les données de prix d’ouverture sur chaque barre de minutes.

Le principe de graphique en soi est assez simple :

  • Nous prenons un point de départ, c’est-à-dire le prix d’ouverture de la barre de première minute.
  • De plus, si le prix déplace la distance égale à la taille de la boîte multipliée par le montant d’inversion, ou plus, nous dessinons les symboles respectifs (O pour un mouvement vers le bas et X pour un mouvement vers le haut). Les données sur le prix du dernier symbole sont stockées pour un graphique ultérieur.
  • Dans le cas où le prix se déplace de la taille de la boîte dans la même direction, un symbole correspondant est dessiné.
  • De plus, en cas d’inversion de prix, le calcul sera basé sur le prix du dernier symbole, plutôt que sur le prix le plus élevé de la paire. En d’autres termes, si le mouvement de prix ne fait pas plus de 50% de la taille de la boîte, il est simplement ignoré.

Déterminons maintenant le style de graphique en Points et Figures. Le langage MQL5 prend en charge sept styles de traçage d’indicateurs : ligne, section (segment), histogramme, flèche (symbole), zone remplie (canal rempli), barres et chandeliers japonais.

Les flèches (symboles) seraient parfaites pour une représentation visuelle idéale, mais ce style nécessite un nombre variable de tampons d’indicateurs (ce qui n’est tout simplement pas pris en charge dans MQL5) ou un nombre énorme de ceux-ci, car le traçage de chaque X ou O dans une colonne nécessite un tampon d’indicateur distinct. Cela signifie que si vous décidez d’utiliser ce style, vous devez définir la volatilité et disposer de ressources mémoire suffisantes.

Nous avons donc opté pour les chandeliers japonais comme style graphique, précisément, les chandeliers japonais colorés. Différentes couleurs sont censées être utilisées pour différencier les colonnes de X des colonnes de O. Ainsi, l’indicateur n’a besoin que de cinq tampons, ce qui permet une utilisation efficace des ressources disponibles.

Les colonnes sont divisées en tailles de boîte à l’aide de lignes horizontales. Le résultat que nous obtenons est tout à fait décent :

Fig. 2. Graphique à l’aide de l’indicateur EURUSD sur la période quotidienne.

Fig. 2. Graphique à l’aide de l’indicateur EURUSD sur la période quotidienne.

 

Algorithme de l’indicateur

Tout d’abord, nous devons déterminer les paramètres d’entrée de l’indicateur. Étant donné que le graphique en Points et Figures ne tient pas compte du temps et que nous utilisons des données pour tracer à partir des barres de minutes, nous devons déterminer la quantité de données à traiter afin de ne pas utiliser inutilement les ressources du système. De plus, il ne sert à rien de tracer un graphique en Points et Figures en utilisant tout l’historique. Nous introduisons donc le premier paramètre - l’historique. Il prendra en considération le nombre de barres de minutes pour le calcul.

En outre, nous devons déterminer la taille de la boîte et le montant de l’inversion. À cette fin, nous introduirons les variables Cell et CellForChange, respectivement. Nous apporterons également le paramètre de couleur pour les X, ColorUp, et pour les O, ColorDown. Et enfin, le dernier paramètre sera la couleur de la ligne - LineColor.

// +++ Program start +++
//+------------------------------------------------------------------+
//|                                                         APFD.mq5 |
//|                                            Aktiniy ICQ:695710750 |
//|                                                    ICQ:695710750 |
//+------------------------------------------------------------------+
#property copyright "Aktiniy ICQ:695710750"
#property link      "ICQ:695710750"
#property version   "1.00"
//--- Indicator plotting in a separate window
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1
//--- plot Label1
#property indicator_label1  "APFD"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_style1  STYLE_SOLID
#property indicator_color1  clrRed,clrGold
#property indicator_width1  1
//--- Set the input parameters
input int   History=10000;
input int   Cell=5;
input int   CellForChange=3;
input color ColorUp=clrRed;
input color ColorDown=clrGold;
input color LineColor=clrAqua;
//--- Declare indicator buffers
double CandlesBufferOpen[];
double CandlesBufferHigh[];
double CandlesBufferLow[];
double CandlesBufferClose[];
double CandlesBufferColor[];
//--- Array for copying calculation data from the minute bars
double OpenPrice[];
// Variables for calculations
double PriceNow=0;
double PriceBefore=0;
//--- Introduce auxiliary variables
char   Trend=0;      // Direction of the price trend
double BeginPrice=0; // Starting price for the calculation
char   FirstTrend=0; // Direction of the initial market trend
int    Columns=0;    // Variable for the calculation of columns
double InterimOpenPrice=0;
double InterimClosePrice=0;
double NumberCell=0; // Variable for the calculation of cells
double Tick=0;       // Tick size
double OldPrice=0;   // Value of the last calculation price
//--- Create arrays to temporary store data on column opening and closing prices
double InterimOpen[];
double InterimClose[];
// +++ Program start +++

Considérons maintenant la fonction OnInit(). Il liera les tampons d’indicateurs aux tableaux unidimensionnels. Nous allons également régler la valeur de l’indicateur sans rendu pour un affichage plus précis et

calculer la valeur de la variable auxiliaire Tick (taille d’une tick) pour les calculs. En outre, nous définirons le schéma de couleurs et l’ordre d’indexation dans les tampons d’indicateurs en tant que séries chronologiques. Ceci est nécessaire pour calculer facilement les valeurs de l’indicateur.

// +++ The OnInit function +++
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,CandlesBufferOpen,INDICATOR_DATA);
   SetIndexBuffer(1,CandlesBufferHigh,INDICATOR_DATA);
   SetIndexBuffer(2,CandlesBufferLow,INDICATOR_DATA);
   SetIndexBuffer(3,CandlesBufferClose,INDICATOR_DATA);
   SetIndexBuffer(4,CandlesBufferColor,INDICATOR_COLOR_INDEX);
//--- Set the value of the indicator without rendering
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//--- Calculate the size of one tick
   Tick=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE);
//--- Set the color scheme
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,ColorUp);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,ColorDown);
//--- Set the indexing order in arrays as time series
   ArraySetAsSeries(CandlesBufferClose,true);
   ArraySetAsSeries(CandlesBufferColor,true);
   ArraySetAsSeries(CandlesBufferHigh,true);
   ArraySetAsSeries(CandlesBufferLow,true);
   ArraySetAsSeries(CandlesBufferOpen,true);
//--- Check the input parameter for correctness
   if(CellForChange<2)
      Alert("The CellForChange parameter must be more than 1 due to plotting peculiarities");
//---
   return(0);
  }
// +++ The OnInit function +++

Nous avons atteint le "cœur" même de l’indicateur, la fonction OnCalculate() où les calculs seront effectués. Les calculs des valeurs des indicateurs sont décomposés en six fonctions de base qui seront appelées à partir de OnCalculate(). Jetons-y un coup d’œil :

1.  Fonction de copie de données

Cette fonction copie les données des barres de minutes dans un tableau pour les calculs. Tout d’abord, nous redimensionnons le tableau récepteur, puis nous y copions les prix d’ouverture à l’aide de la fonction CopyOpen().

//+------------------------------------------------------------------+
//| Function for copying data for the calculation                    |
//+------------------------------------------------------------------+
int FuncCopy(int HistoryInt)
  {
//--- Resize the array for copying calculation data
   ArrayResize(OpenPrice,(HistoryInt));
//--- Copy data from the minute bars to the array
   int Open=CopyOpen(Symbol(),PERIOD_M1,0,(HistoryInt),OpenPrice);
//---
   return(Open);
  }

2.  Fonction de calcul du nombre de colonnes

Cette fonction calcule le nombre de colonnes pour les graphiques en Points et Figures.

Les calculs sont effectués dans une boucle itérant sur le nombre de barres sur la période de temps minute qui ont été copiées dans la fonction ci-dessus. La boucle elle-même se compose de trois blocs principaux pour différents types de tendances:

  •  0 - tendance indéfinie.
  •  1 - tendance haussière.
  • -1 - tendance baissière.

La tendance indéfinie ne sera utilisée qu’une seule fois pour déterminer le mouvement initial des prix. La direction du mouvement des prix sera déterminée lorsque la valeur absolue de la différence entre le marché actuel et le prix initial dépassera la valeur de la taille de la boîte multipliée par le montant d’inversion.

S’il y a une rupture à la baisse, la tendance initiale sera identifiée comme une tendance baissière et une entrée correspondante sera faite dans la variable Tendance. Une tendance haussière est identifiée exactement de la manière opposée. En outre, la valeur de la variable pour le nombre de colonnes, ColumnsInt, sera augmentée.

Une fois la tendance actuelle identifiée, nous fixons deux conditions pour chaque direction. Si le prix continue d’évoluer dans la direction de la tendance actuelle par la taille de la boîte, la valeur de la variable ColumnsInt restera inchangée. Si le prix est inversé par la taille de la boîte multipliée par le montant d’inversion, une nouvelle colonne apparaîtra et la valeur de la variable ColumnsInt augmentera d’un.

Et ainsi de suite jusqu’à ce que toutes les colonnes soient identifiées. 

Pour arrondir le nombre de cellules dans la boucle, nous utiliserons la fonction MathRound() qui nous permet d’arrondir les valeurs résultantes aux entiers les plus proches. En option, cette fonction peut être remplacée par la fonction MathFloor() (arrondi à l’entier le plus proche) ou la fonction MathCeil() (arrondi à l’entier le plus proche), selon le tracé requis.

//+------------------------------------------------------------------+
//| Function for calculating the number of columns                   |
//+------------------------------------------------------------------+
int FuncCalculate(int HistoryInt)
  {
   int ColumnsInt=0;

//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   FirstTrend=0;            // Direction of the initial market trend
   Columns=0;               // Variable for the calculation of columns
   InterimOpenPrice=0;
   InterimClosePrice=0;
   NumberCell=0;            // Variable for the calculation of cells
//--- Loop for the calculation of the number of main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
           }
         BeginPrice=InterimClosePrice;
         ColumnsInt++;
         FirstTrend=Trend;
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice+(Cell*Tick);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice-(Cell*Tick);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
     }
//---
   return(ColumnsInt);
  }

3.  Fonction de coloration des colonnes

Cette fonction est destinée à colorier les colonnes selon les besoins à l’aide du jeu de couleurs prédéfini. À cette fin, nous allons écrire une boucle itérant sur le nombre de colonnes et définir les couleurs appropriées sur les colonnes pairs et impaires, en tenant compte de la valeur de tendance initiale (colonne initiale).

//+------------------------------------------------------------------+
//| Function for coloring columns                                    |
//+------------------------------------------------------------------+
int FuncColor(int ColumnsInt)
  {
   int x;
//--- Fill the buffer of colors for drawing
   for(x=0; x<ColumnsInt; x++)
     {
      if(FirstTrend==-1)
        {
         if(x%2==0) CandlesBufferColor[x]=1; // All even buffers of color 1
         if(x%2>0) CandlesBufferColor[x]=0;  // All odd buffers of color 0
        }
      if(FirstTrend==1)
        {
         if(x%2==0) CandlesBufferColor[x]=0; // All odd buffers of color 0
         if(x%2>0) CandlesBufferColor[x]=1;  // All even buffers of color 1
        }
     }
//---
   return(x);
  }

4.  Fonction de détermination de la taille de la colonne

Une fois que nous avons déterminé le nombre de colonnes à utiliser et défini les couleurs nécessaires, nous devons déterminer la hauteur des colonnes. Pour ce faire, nous allons créer des tableaux temporaires, InterimOpen[] et InterimClose[], dans lesquels nous stockerons les prix d’ouverture et de clôture pour chaque colonne. La taille de ces tableaux sera égale au nombre de colonnes.

Ensuite, nous aurons une boucle qui est presque complètement identique à la boucle de la fonction FuncCalculate(), sa différence étant qu’en dehors de tout ce qui précède, elle stocke également les prix d’ouverture et de clôture pour chacune des colonnes. Cette séparation est mise en œuvre de manière à connaître à l’avance le nombre de colonnes du graphique. Théoriquement, nous pourrions initialement définir un nombre sciemment plus élevé de colonnes pour l’allocation de mémoire de tableau et ne faire qu’avec une seule boucle. Mais dans ce cas, nous aurions une plus grande utilisation des ressources de mémoire.

Examinons maintenant plus en détail la détermination de la hauteur de la colonne. Une fois que le prix a déplacé la distance égale au nombre requis de tailles de boîte, nous calculons leur nombre, en l’arrondissant à l’entier le plus proche. Ensuite, nous ajoutons le nombre total de tailles de boîte de la colonne actuelle au prix d’ouverture de la colonne, obtenant ainsi le prix de clôture de la colonne, qui devient également le dernier prix utilisé. Il sera impliqué dans toutes les actions ultérieures.

//+------------------------------------------------------------------+
//| Function for determining the column size                         |
//+------------------------------------------------------------------+
int FuncDraw(int HistoryInt)
  {
//--- Determine the sizes of temporary arrays
   ArrayResize(InterimOpen,Columns);
   ArrayResize(InterimClose,Columns);
//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   NumberCell=0;            // Variable for the calculation of cells
   int z=0;                 // Variable for indices of temporary arrays
//--- Loop for filling the main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits()); // Normalize the number of decimal places
            Trend=1;
           }
         BeginPrice=InterimClose[z];
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice+(Cell*Tick);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice-(Cell*Tick);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
     }
//---
   return(z);
  }

5.  Fonction d’inversion de tableau

La fonction inverse les données de tableau de colonnes obtenues afin d’afficher davantage le graphique par programme de droite à gauche. L’inversion du tableau est effectuée en boucle, les valeurs High et Low étant attribuées aux chandeliers. Ceci est fait parce que l’indicateur n’est affiché que pour les chandeliers pour lesquels toutes les valeurs de tampon de l’indicateur ne sont pas égales à zéro.

//+------------------------------------------------------------------+
//| Function for array reversal                                      |
//+------------------------------------------------------------------+
int FuncTurnArray(int ColumnsInt)
  {
//--- Variable for array reversal
   int d=ColumnsInt;
   for(int x=0; x<ColumnsInt; x++)
     {
      d--;
      CandlesBufferOpen[x]=InterimOpen[d];
      CandlesBufferClose[x]=InterimClose[d];
      if(CandlesBufferClose[x]>CandlesBufferOpen[x])
        {
         CandlesBufferHigh[x]=CandlesBufferClose[x];
         CandlesBufferLow[x]=CandlesBufferOpen[x];
        }
      if(CandlesBufferOpen[x]>CandlesBufferClose[x])
        {
         CandlesBufferHigh[x]=CandlesBufferOpen[x];
         CandlesBufferLow[x]=CandlesBufferClose[x];
        }
     }
//---
   return(d);
  }

6.  Fonction de dessin de lignes horizontales

Cette fonction crée une grille de "boîtes" à l’aide de lignes horizontales (objets). Au début de la fonction, nous déterminons les valeurs de prix maximum et minimum à partir du tableau de données de calcul. Ces valeurs sont ensuite utilisées pour tracer progressivement les lignes de haut en bas à partir du point de départ.

//+------------------------------------------------------------------+
//| Function for drawing horizontal lines                            |
//+------------------------------------------------------------------+
int FuncDrawHorizontal(bool Draw)
  {
   int Horizontal=0;
   if(Draw==true)
     {
      //--- Create horizontal lines (lines for separation of columns)
      ObjectsDeleteAll(0,ChartWindowFind(),OBJ_HLINE); // Delete all old horizontal lines
      int MaxPriceElement=ArrayMaximum(OpenPrice);     // Determine the maximum price level
      int MinPriceElement=ArrayMinimum(OpenPrice);     // Determine the minimum price level
      for(double x=OpenPrice[0]; x<=OpenPrice[MaxPriceElement]+(Cell*Tick); x=x+(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      for(double x=OpenPrice[0]-(Cell*Tick); x>=OpenPrice[MinPriceElement]; x=x-(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      ChartRedraw();
     }
//---
   return(Horizontal);
  }

Maintenant que nous avons décrit toutes les fonctions de base, voyons l’ordre dans lequel elles sont appelées dans OnCalculate():

  • Démarrer la fonction de copie des données pour les calculs (à condition qu’il n’y ait pas encore de barres calculées).
  • Appeler la fonction de calcul du nombre de colonnes.
  • Déterminer les couleurs des colonnes.
  • Déterminer la taille des colonnes.
  • Appeler la fonction d’inversion des données dans les tableaux.
  • Appeler la fonction de traçage des lignes horizontales qui diviseront les colonnes en « boîtes ».
// +++ Main calculations and plotting +++
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Reverse the array to conveniently get the last price value
   ArraySetAsSeries(close,true);
//---
   if(prev_calculated==0)
     {
      //--- Start the function for copying data for the calculation
      int ErrorCopy=FuncCopy(History);
      //--- In case of error, print the message
      if(ErrorCopy==-1)
        {
         Alert("Failed to copy. Data is still loading.");
         return(0);
        }
      //--- Call the function for calculating the number of columns
      Columns=FuncCalculate(History);
      //--- Call the function for coloring columns
      int ColorCalculate=FuncColor(Columns);
      //--- Call the function for determining column sizes
      int z=FuncDraw(History);
      //--- Start the function for array reversal
      int Turn=FuncTurnArray(Columns);
      //--- Start the function for drawing horizontal lines
      int Horizontal=FuncDrawHorizontal(true);
      //--- Store the value of the last closing price in the variable
      OldPrice=close[0];
     }
//--- If the price is one box size different from the previous one, 
//--- the indicator is recalculated
   if(fabs((OldPrice-close[0])/Tick)>Cell)
      return(0);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
// +++ Main calculations and plotting +++

C’est la fin du code de base de l’indicateur. Mais comme l’indicateur a ses inconvénients en ce qu’il contient des tableaux complexes, il doit parfois être rechargé.

Pour implémenter cela, nous utiliserons la fonction OnChartEvent() qui gère les événements d’appui sur la touche "С" - effacer et la touche  "R" - redessiner. Pour effacer, une valeur nulle est attribuée à l’un des tampons d’indicateur. La fonction de redessiner du graphique représente une répétition des calculs précédents et l’affectation des valeurs aux tampons d’indicateurs.

// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_KEYDOWN)
     {
      //--- 67 - The "C" key code clears the indicator buffer
      if(lparam==67)
        {
         for(int x=0; x<Bars(Symbol(),PERIOD_CURRENT); x++)
            CandlesBufferOpen[x]=0;
         ChartRedraw();
        }
      // 82 - The "R" key code redraws the indicator
      if(lparam==82)
        {
         //--- Start the copying function
         int ErrorCopy=FuncCopy(History);
         //--- In case of error, print the message
         if(ErrorCopy==-1)
            Alert("Failed to copy data.");
         //--- Call the function for calculating the number of columns
         Columns=FuncCalculate(History);
         //--- Call the function for coloring columns
         int ColorCalculate=FuncColor(Columns);
         //--- Call the function for determining column sizes
         int z=FuncDraw(History);
         //--- Start the function for array reversal
         int Turn=FuncTurnArray(Columns);
         //--- Start the function for drawing horizontal lines
         int Horizontal=FuncDrawHorizontal(true);
        }
     }
  }
//+------------------------------------------------------------------+
// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++

Nous pouvons maintenant prendre une profonde respiration car nous avons terminé avec la description de l’algorithme et du code de l’indicateur et pouvons continuer à jeter un coup d’œil à certains modèles de graphiques en Points et Figures qui génèrent des signaux pour l’exécution des transactions.

 

Signaux standard

Il existe deux approches dans le trading et l’analyse des graphiques en Points et Figures: basée sur des modèles et basée sur des lignes de support et de résistance. Ce dernier est particulier en ce sens que les lignes de support et de résistance sont tracées à un angle de 45 degrés (ce n’est pas toujours le cas dans un indicateur conçu car il est tracé à l’aide de chandeliers japonais dont la taille change en fonction de la taille du graphique principal, ce qui peut entraîner une distorsion de l’angle).

Considérons maintenant les modèles en tant que tels :

  1. Le modèle "Double Top" et "Double Bottom".

    "Double Top" se produit lorsque le prix augmente, puis baisse en formant une certaine colonne de O et augmente à nouveau, dépassant la colonne précédente de X d’une taille de boîte. Ce modèle est un signal d’achat.

    "Double Bottom" est l’exact opposé du modèle "Double Top". Il y a une certaine baisse de prix (une colonne de O) suivie d’une colonne de X et d’une autre colonne de O qui tombe une case en dessous de la colonne précédente de O, formant ainsi un signal de vente.

    Fig. 3. Les modèles "Double Top" et "Double Bottom".

    Fig. 3. Les modèles "Double Top" et "Double Bottom".

  2. Les modèles "Triple Top" et "Triple Bottom".

    Ces schémas sont moins fréquents mais représentent des signaux très forts. Essentiellement, ils sont similaires aux modèles "Double Top" et "Double Bottom", étant leur continuation. Avant de transmettre un signal, ils répètent les mouvements des deux modèle ci-dessus.

    Un "Triple Top" se produit lorsque le prix atteint deux fois le même niveau de prix, puis dépasse ce niveau, représentant un signal d’achat.

    Le modèle "Triple Bottom" est opposé à "Triple Top" et se produit lorsque le prix tombe deux fois au même niveau, puis tombe en dessous de ce niveau, transmettant ainsi un signal de vente.

    Fig. 4. Les modèles "Triple Top" et "Triple Bottom".

    Fig. 4. Les modèles "Triple Top" et "Triple Bottom".

  3. Les modèles de "Symmetrical Triangle Breakout" : hausse et baisse.

    Nous nous souvenons tous des modèles d’analyse technique. Le modèle "Symmetrical Triangle Breakout" est similaire à "Symmetrical Triangle" en analyse technique. Une rupture haussière (comme indiqué ci-dessous dans la figure de gauche) est un signal d’achat. Inversement, une dégradation à la baisse est un signal de vente (figure à droite).

    Fig. 5. "Symmetrical Triangle Breakout": à la hausse et à la baisse..

    Fig. 5. "Symmetrical Triangle Breakout": à la hausse et à la baisse.

  4. Les modèles"Bullish Catapult" et "Bearish Catapult".

    Les "Catapults" sont en quelque sorte similaires aux modèles d’analyse technique "Ascending Triangle" et "Descending Triangle". Leurs signaux sont essentiellement similaires - dès que le prix se casse au-dessus ou au-dessous du côté parallèle du triangle, un signal d’achat ou de vente se produit, respectivement. Dans le cas de la "Bullish Catapult", le prix éclate ci-dessus, qui est un signal d’achat (figure à gauche), tandis que dans le cas de la "Bearish Catapult", le prix éclate en dessous, qui est un signal de vente (figure à droite).

    Fig. 6. Les modèles "Bullish Catapult" et "Bearish Catapult".

    Fig. 6. Les modèles "Bullish Catapult" et "Bearish Catapult".

  5. Le modèle "45-Degree Trend Line".

    Le modèle "45-Degree Trend Line" crée une ligne de support ou de résistance. S’il y a une rupture de cette ligne, nous obtenons soit un signal de vente (comme indiqué dans la figure de droite), soit un signal d’achat (comme dans la figure de gauche).

    Fig. 7. Le modèle "45-Degree Trend Line".

    Fig. 7. Le modèle "45-Degree Trend Line".

Nous avons examiné les modèles et les signaux standard des graphiques en Points et Figures. Trouvons maintenant quelques-uns d’entre eux dans le tableau des indicateurs fourni au début de l’article:

Fig. 8. Identification des modèles dans le graphique en Points et Figures.

Fig. 8. Identification des modèles dans le graphique en Points et Figures.

 

Conclusion

Nous avons atteint l’étape finale de l’article. Je voudrais dire ici que le graphique en Points et Figures n’est pas perdu dans le temps et est toujours activement utilisé, ce qui prouve une fois de plus sa valeur.

L’indicateur développé, bien qu’il ne soit pas dépourvu d’inconvénients, tels que la graphique par blocs au lieu des X et O habituels et l’impossibilité d’être testé (ou plutôt, un fonctionnement incorrect) dans le strategy Tester, donne des résultats graphiques assez précis.

Je tiens également à noter que cet algorithme, dans une version légèrement modifiée, peut potentiellement être utilisé pour le graphique Renko, ainsi que pour l’intégration des deux types de graphiques dans un seul code avec des options de menu, vous permettant de sélectionner, le cas échéant. Je n’exclus pas la possibilité de tracer le graphique directement dans la fenêtre principale, ce qui nécessitera à nouveau une légère modification du code.

En général, le but de cet article était de partager mes idées concernant le développement des indicateurs. Comme il s’agit de mon premier article, j’apprécierai tous les commentaires ou commentaires. Merci de l’intérêt que vous portez à mon article !

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/656

Fichiers joints |
apfd.mq5 (18.85 KB)
Le réseau MQL5 Cloud Network : Calculez-vous toujours ? Le réseau MQL5 Cloud Network : Calculez-vous toujours ?
Cela fera bientôt un an et demi que le réseau MQL5 Cloud Network a été lancé. Cet événement de pointe a inauguré une nouvelle ère de trading algorithmique - désormais, en quelques clics, les traders peuvent disposer de centaines et de milliers de cœurs de calcul pour l'optimisation de leurs stratégies de trading.
Débogage des programmes MQL5 Débogage des programmes MQL5
Cet article est principalement destiné aux programmeurs qui ont déjà appris le langage mais qui ne maîtrisent pas encore complètement le développement du programme. Il révèle quelques techniques de débogage et présente une expérience combinée de l'auteur et de nombreux autres programmeurs.
MQL5 Cookbook : Gestion des événements du graphique typiques MQL5 Cookbook : Gestion des événements du graphique typiques
Cet article examine les événements du graphique typiques et inclut des exemples de leur traitement. Nous nous concentrerons sur les événements de souris, les frappes au clavier, la création/modification/suppression d'un objet graphique, le clic de souris sur un graphique et sur un objet graphique, le déplacement d'un objet graphique avec une souris, la finition de l'édition de texte dans un champ de texte, ainsi que sur les événements de modification de graphique. Un exemple de programme MQL5 est fourni pour chaque type d'événement considéré.
MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations
Quelle que soit la stratégie de trading que vous utilisez, il y aura toujours une question de quels paramètres choisir pour assurer des bénéfices futurs. Cet article donne un exemple d'Expert Advisor avec la possibilité d'optimiser plusieurs paramètres de symboles en même temps. Cette méthode est destinée à réduire l'effet du surajustement des paramètres et à traiter les situations où les données d'un seul symbole ne sont pas suffisantes pour l'étude.