
Indicateur pour le graphique en Points et Figures
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).
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.
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 :
-
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".
-
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".
-
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.
-
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".
-
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".
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.
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





- 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