Indicateur pour la cartographie Kagi
Introduction
L'article "Indicateur pour la cartographie en Point et en Figure" a décrit l'une des méthodes de programmation permettant de créer Graphique en Point et Figure. Ce graphique est connu depuis le 19ème siècle. Cependant, ce n'est pas le seul graphique du passé lointain. Un autre représentant notable des premiers types de représentation du marché financier est Graphique Kagi. Ce graphique sera discuté dans le présent article.
The Stock exchange - institution financière peu familière au Japon du 19ème siècle - a été créée en mai 1878. Il est connu sous le nom de Tokyo Stock Exchange de nos jours. Cet événement a joué un rôle essentiel dans la création et le développement ultérieur des graphiques Kagi. L'Europe et les États-Unis ont découvert les graphiques Kagi après la publication de Steve Nison "Au-delà des chandeliers : Nouvelles techniques japonaises de cartographie révélées" en 1994.
Les mots japonais "Kagi" désignent une clé en forme de L qui était utilisée au moment de l'élaboration du graphique. En outre, il existe une version modifiée du nom - "key chart". Dans "Au-delà des chandeliers" de Steve Nison, vous pouvez également trouver des noms alternatifs pour le graphique : graphique de fourchette de prix, graphique en crochet, graphique en delta ou en chaîne.
Quelle est la particularité de ce graphique ? Sa principale caractéristique est qu'il ignore l'échelle de temps en ne laissant que le prix (contrairement aux chandeliers, barres et lignes japonais). Ainsi, le graphique cache des fluctuations de prix inconsidérables ne laissant que les plus importantes.
Le graphique représente un ensemble de lignes Yang épaisses et Yin fines se remplaçant en fonction de la situation du marché. Dans le cas où le marché évolue dans la même direction, la ligne est prolongée pour atteindre une nouvelle fourchette de prix. Cependant, si le marché se retourne et atteint un montant prédéfini, la ligne Kagi est tracée en sens inverse dans la nouvelle colonne. Le montant prédéfini est défini soit en points (généralement utilisé pour les paires de devises), soit en pourcentage du prix actuel (généralement utilisé pour les actions). L'épaisseur de la ligne varie en fonction de la percée élevée ou faible la plus proche.
1. Exemple de cartographie
Utilisons les données historiques sur l'EURUSD, H1 du 8 au 11 octobre.
Un exemple d'imagerie standard avec un seuil inversé de 15 points est présenté sur la Fig. 1 :
Fig. 1. Graphique de Kagi, EURUSD H1
Comme nous pouvons le voir, le prix a commencé à baisser à 17h00. Le mouvement de baisse s'est poursuivi jusqu'à 21h00. À 22h00, le prix monte de 1,3566 et clôture à 1,3574. Autrement dit, le prix passe à 11 points. Ce n'est pas suffisant pour un retournement, mais le nouveau plus bas n'a pas été atteint non plus. Les deux heures suivantes, le prix reste plat et enfin, à 01h00 (9 octobre), nous assistons à un fort mouvement haussier, qui est clôturé à 1,3591 comprenant 25 points (1,3591-1,3566). Cela signifie que le prix s'est inversé.
La tendance haussière se poursuit l'heure suivante. Le prix atteint 1,3599 renforçant l'épaisse ligne Yang. À 03h00, le prix chute fortement pour clôturer à 1,3578, soit 21 points par rapport au précédent sommet (1,3599-1,3578). C'est plus que suffisant pour le retournement. La ligne descend mais conserve sa forme (ligne Yang épaisse).
Jusqu'à 16h00, le prix baisse et finalement il franchit le minimum le plus proche et passe d'une ligne Yang épaisse à une ligne Yin mince. La valeur basse de 1,3566 mentionnée précédemment avait servi de prix décisif ici. Le prix continue de se déplacer comme une ligne Yin et est changé en Yang à 14h00 le 10 octobre, franchissant le plus haut le plus proche de 1,3524 formé à 23h00 (9 octobre). Ce petit exemple montre comment le graphique de Kagi est formé.
2. Principe de cartographie de l’indicateur Kagi
Afin de rendre l'indicateur indépendant de la période actuelle, il a été décidé de copier séparément les données de la période à laquelle l'indicateur était censé être formé, puis de créer l'indicateur à partir des données obtenues.
Cela permet d'examiner plusieurs périodes simultanément sur un seul graphique, ce qui élargit les limites de l'analyse technique sur les graphiques Kagi. L'indicateur lui-même est situé dans une fenêtre séparée, mais il est également possible d'afficher des données sur le graphique principal. En d'autres termes, la formation de base (apparence standard ou modifiée) est effectuée dans la fenêtre de l'indicateur. L'indicateur est copié dans le graphique principal, des marques de prix et de temps (selon les paramètres) sont également dessinées.
Comme mentionné précédemment, l'indicateur dessine le graphique à la fois en version standard et modifiée. La norme a été décrite ci-dessus. Considérons maintenant la version modifiée.
Je ne sais pas si c'est une idée nouvelle, mais je n'ai pas entendu parler d'une telle version. L'idée du filtre supplémentaire est que non seulement les points inversés, mais chaque mouvement du graphique est maintenant filtré. En d'autres termes, le prix doit se déplacer sur une distance spécifiée pour que le nouveau haut ou bas (à ne pas confondre avec l'épaule/la taille) se forme. Généralement, partout où le prix évolue, il doit d'abord couvrir une distance spécifiée. Après cela, il est défini s'il s'agissait d'une poursuite ou d'un retournement de tendance.
La figure 2 montre comment fonctionne le principe. L'aspect modifié du graphique est affiché en bleu, tandis que l'aspect standard est en rouge. Comme nous pouvons le voir, l'apparence modifiée répond aux changements de mouvement des prix plus lentement en filtrant la plupart des signaux mineurs.
Fig. 2. Versions modifiée (ligne bleue) et standard (ligne rouge) de la création du graphique Kagi
Outre le graphique de Kagi, l'indicateur fournit des éléments supplémentaires à la fois dans la fenêtre de l'indicateur et dans le graphique principal.
Selon les réglages, les marques peuvent être placées dans la fenêtre d'indicateur. Ces marques fournissent des données sur les prix de retournement. La même fonction est mise en œuvre en utilisant des niveaux de prix, qui (selon les paramètres) peuvent se répartir uniformément le long de la fenêtre sur toute la fourchette de prix utilisée pour former l'indicateur ou à chaque retournement de graphique. Les couleurs peuvent être définies en trois versions : selon le type de retournement (haut - bas), type de ligne (Yin - Yang) ou pas de changement de couleur.
Les marques de prix de retournement, y compris temporaires, sont fournies sur le graphique principal. Ces marques (selon les réglages) peuvent être d'une seule couleur ou changer de couleur en fonction des couleurs des lignes Yin ou Yang.
L'ensemble du code indicateur est implémenté à l'aide des fonctions communiquant entre elles via des variables globales.
Le code peut être divisé en trois fonctions principales et onze fonctions supplémentaires. La charge principale des calculs et des remplissages de tampons des constructions graphiques de base et des tableaux de tampons supplémentaires repose sur la fonction de formation du graphique de Kagi dans la fenêtre d'indicateur. Les deux autres fonctions sont chargées de fournir les données : la première copie les données de temps, tandis que l'autre - les données sur les prix de chaque barre de la période sélectionnée.
Les fonctions auxiliaires restantes sont chargées d'effectuer toutes les constructions, de supprimer les objets, de décharger l'indicateur accompagné de la suppression de tous les objets indicateurs, de calculer les paramètres de retournement, de tracer des repères sur le graphique principal et la fenêtre de l'indicateur, de créer des objets graphiques de type "Ligne de tendance" , dessinant Kagi sur le graphique principal, ainsi que définissant l'arrivée de la nouvelle barre pour lancer la formation de l'indicateur.
3. Code d'indicateur et algorithme
Examinons maintenant en détail le code indicateur et l'algorithme de sa formation. Le code est assez volumineux et il peut être assez difficile pour les programmeurs novices de le comprendre. Les fonctions communiquant entre elles via les variables globales rendent le code assez confus. Dans cette partie de l'article, j'expliquerai chaque fonction et partie du code séparément. Tout d'abord, je décrirai les paramètres de l'indicateur, puis il y aura des clarifications concernant les fonctions initiales de copie de données, le calcul des paramètres de retournement, la fonction principale de la formation et du calcul du diagramme de Kagi et d'autres fonctions auxiliaires.
3.1. Paramètres d'entrée de l'indicateur
Le code commence par la déclaration de l'indicateur dans une fenêtre séparée, ainsi que de 12 tampons et 8 constructions graphiques d'indicateurs. Tout d'abord, définissons pourquoi 8 constructions graphiques, dont deux "histogrammes" et six "lignes", ont été utilisées. Chaque "histogramme" construit sa propre ligne verticale. L'une des lignes est responsable de la ligne Yin, tandis que l'autre est responsable de la ligne Yang.
Le cas est un peu plus compliqué avec les "lignes", car il y en a trois pour chaque ligne. Cela est dû au fait que la ligne est tracée s'il y a un autre point tracé près du premier. En d'autres termes, nous n'avons besoin que de deux constructions graphiques de type "ligne" à faire pivoter pour dessiner deux lignes adjacentes l'une à l'autre. Cependant, si nous avons besoin de ces lignes pour sauter des points nécessaires, nous avons besoin que la troisième construction soit tournée avec les deux autres.
Ceci est expliqué dans la figure 3, où vous pouvez voir ce qui se passe si seulement deux constructions graphiques de type "ligne" sont utilisées :
Fig. 3. Exemple d'utilisation de deux et trois constructions graphiques de type "ligne" pour afficher les lignes des épaules et de la taille
Ensuite, le menu des paramètres est créé. Il y a cinq énumérations ici (examinons-les dans les paramètres d'entrée).
Le premier paramètre d'entrée "période" est une période à laquelle la construction est effectuée, il est suivi de "période à_refaire" - période de mise à jour de la construction du graphique et le dernier paramètre de temps est "données_début" - l'heure à laquelle la construction commence.
Ces paramètres sont suivis par la construction de graphique et des étiquetages supplémentaires :
- kagi_type – type de construction de graphique défini par l'utilisateur, standard ou modifié ;
- price_type – type de prix utilisé pour la construction : Fermé, Ouvert, Haut et Bas ;
- type_doorstep – type de retournement utilisé : point et pourcentage ;
- doorstep – valeur de retournement (spécifiée en points ou en pourcentage en fonction du paramètre ci-dessus) ;
- color_yin – Couleur de la ligne Yin dans la fenêtre de l'indicateur ;
- color_yang – Couleur de la ligne Yang dans la fenêtre de l'indicateur ;
- width_yin – Largeur de ligne Yin dans la fenêtre d'indicateur ;
- width_yang – Largeur de ligne Yang dans la fenêtre de l'indicateur ;
- levels_on_off - si les niveaux de prix doivent être dessinés dans la fenêtre d'indicateur ;
- levels_type – types de niveaux de prix dans la fenêtre d'indicateur. Vous avez le choix entre deux valeurs : à chaque retournement ou uniformément sur toute la fourchette de prix ;
- level_number – nombre de niveaux de prix dans la fenêtre d'indicateur ;
- level_change_color – permet de changer la couleur des lignes de niveau de prix ; les options sont les retournements supérieurs et inférieurs, les lignes Yin et Yang ou aucun changement ;
- level_first_color – la première couleur d'un niveau de prix ;
- level_second_color – la deuxième couleur d'un niveau de prix ;
- label_1 - dessin des étiquettes de prix de retournement du graphique dans la fenêtre de l'indicateur ;
- label_1_number – nombre d'étiquettes affichées dans la fenêtre de l'indicateur ;
- label_1_color – couleur des étiquettes de prix dans la fenêtre de l'indicateur ;
- label_2 - dessin des étiquettes de prix sur le graphique principal ;
- label_2_color – couleur de l'étiquette sur le graphique principal ;
- time_line_draw - dessin des lignes de temps de retournement sur le graphique principal ;
- time_separate_windows - dessin de la suite des lignes de temps de retournement à partir du graphique principal ;
- time_line_change_color - change la couleur de la ligne de temps en fonction de l'étiquette de retournement sur la ligne Yin ou Yang ;
- time_first_color – la première couleur de la ligne de temps sur le graphique principal ;
- time_second_color – la deuxième couleur de la ligne de temps sur le graphique principal ;
- kagi_main_chart - si Kagi doit être dessiné sur le graphique principal ;
- color_yin_main – Couleur de la ligne Yin sur le graphique principal ;
- color_yang_main – Couleur de la ligne Yang sur le graphique principal ;
- width_yin_main – Largeur de la ligne Yin sur le graphique principal ;
- width_yang_main – Largeur de ligne Yang sur le graphique principal ;
- magic_numb – numéro magique utilisé pour la construction d'objets et leur suppression, ainsi que dans le nom de l'indicateur afin de lancer plusieurs indicateurs sur un même graphique.
Ces paramètres sont à leur tour suivis des déclarations des tampons indicateurs, des tampons auxiliaires pour stocker les valeurs de prix et de temps, des variables auxiliaires (stop_data, bars_copied, bars_copied_time, copy_history, copy_time), des tableaux pour stocker des données sur quelle ligne Yin ou Yang le changement de le mouvement du graphique s'est produit, l'heure et le prix de ce changement, le prix central (si Yin est remplacé par Yang sur la barre ou vice versa). Enfin, l'une des variables globales les plus utilisées contenant des données sur le nombre de changements de mouvement du graphique "а" est déclarée.
//+------------------------------------------------------------------+ //| BKCV.mq5 | //| Azotskiy Aktiniy ICQ:695710750 | //| https://www.mql5.com/ru/users/Aktiniy | //+------------------------------------------------------------------+ //--- Build Kagi Chart Variable #property copyright "Azotskiy Aktiniy ICQ:695710750" #property link "https://www.mql5.com/en/users/Aktiniy" #property version "1.00" #property description "Build Kagi Chart Variable" #property description " " #property description "This indicator makes drawing a chart Kagi as a matter of indicator window, and in the main chart window" #property indicator_separate_window #property indicator_buffers 12 #property indicator_plots 8 //--- plot Yin #property indicator_label1 "Yin" #property indicator_type1 DRAW_HISTOGRAM2 #property indicator_color1 clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot Yin1 #property indicator_label2 "Yin1" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- plot Yin2 #property indicator_label3 "Yin2" #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed #property indicator_style3 STYLE_SOLID #property indicator_width3 1 //--- plot Yin3 #property indicator_label4 "Yin3" #property indicator_type4 DRAW_LINE #property indicator_color4 clrRed #property indicator_style4 STYLE_SOLID #property indicator_width4 1 //--- plot Yang #property indicator_label5 "Yang" #property indicator_type5 DRAW_HISTOGRAM2 #property indicator_color5 clrRed #property indicator_style5 STYLE_SOLID #property indicator_width5 2 //--- plot Yang1 #property indicator_label6 "Yang1" #property indicator_type6 DRAW_LINE #property indicator_color6 clrRed #property indicator_style6 STYLE_SOLID #property indicator_width6 2 //--- plot Yang2 #property indicator_label7 "Yang2" #property indicator_type7 DRAW_LINE #property indicator_color7 clrRed #property indicator_style7 STYLE_SOLID #property indicator_width7 2 //--- plot Yang3 #property indicator_label8 "Yang3" #property indicator_type8 DRAW_LINE #property indicator_color8 clrRed #property indicator_style8 STYLE_SOLID #property indicator_width8 2 //--- Enumerations as input data (for more attractive setting) //--- Kagi charting type enum kagi_type_enum { classic=0, // Classic modified=1, // Modified }; //--- Type of the price used for construction enum price_type_enum { c=0, // Close o=1, // Open h=2, // High l=3, // Low }; //--- Type of the used reversal enum type_doorstep_enum { point=0, // Point procent=1, // Percent }; //--- Type of levels location enum levels_type_enum { cor=0, // Cornering equ=1, // Equal distance }; //--- Level colors change type (works when "Type of levels location"="Cornering") enum levels_change_color_enum { up_down=0, // Up & Down yin_yang=1, // Yin & Yang no=2, // Don't change }; //--- input parameters input ENUM_TIMEFRAMES period=PERIOD_CURRENT; // Calculation period to build the chart input ENUM_TIMEFRAMES period_to_redraw=PERIOD_M1; // Refresh period chart input datetime start_data=D'2013.07.10 00:00:00'; // Start time to build the chart input kagi_type_enum kagi_type=classic; // The type to build Kagi chart input price_type_enum price_type=c; // Price used to build chart input type_doorstep_enum type_doorstep=point; // Type calculate doorstep input double doorstep=25; // Doorstep reversal input color color_yin=clrRed; // Color Yin line (indicator window) input color color_yang=clrRed; // Color Yang line (indicator window) input char width_yin=1; // Width Yin line (indicator window) input char width_yang=2; // Width Yang line (indicator window) input bool levels_on_off=false; // Draw level (indicator window) input levels_type_enum levels_type=cor; // Type of drawing levels (indicator window) input uint levels_number=6; // Number of levels (indicator window) input levels_change_color_enum levels_change_color=up_down; // Type change color of levels (indicator window) input color levels_first_color=clrBeige; // The first color of level (indicator window) input color levels_second_color=clrCoral; // The second color of level (indicator window) input bool label_1=true; // Draw price label on (indicator window) input uint label_1_number=10; // The number of labels (indicator window) input color label_1_color=clrGreenYellow; // The color of labels (indicator window) input bool label_2=true; // Draw price label on (main chart) input color label_2_color=clrGreenYellow; // The color of labels (main chart) input bool time_line_draw=true; // Draw a timeline reversal (main chart) input bool time_separate_windows=false; // Draw a timeline reversal on indicator window input bool time_line_change_color=true; // Different color timeline on the Yin and Yang lines (main chart) input color time_first_color=clrRed; // The first color of timeline (main chart) input color time_second_color=clrGreenYellow; // The second color of timeline (main chart) input bool kagi_main_chart=true; // Draw Kagi on main chart (main chart) input color color_yin_main=clrRed; // Color Yin line (main chart) input color color_yang_main=clrRed; // Color Yang line (main chart) input char width_yin_main=1; // Width Yin line (main chart) input char width_yang_main=2; // Width Yang line (main chart) input long magic_numb=65758473787389; // The magic number for drawing objects //--- indicator buffers double YinBuffer1[]; double YinBuffer2[]; double Yin1Buffer[]; double Yin2Buffer[]; double Yin3Buffer[]; double YangBuffer1[]; double YangBuffer2[]; double Yang1Buffer[]; double Yang2Buffer[]; double Yang3Buffer[]; //--- additional variables double Price[]; // Buffer for storing the copied price data double Time[]; // Buffer for storing the copied time data //--- datetime stop_data; // Current time int bars_copied=0; // Number of the already copied bars from the initial date int bars_copied_time; // Number of the already copied bars having the initial date bool copy_history=false; // Price history copying result bool copy_time=false; // Time history copying result //--- datetime time_change[]; // Array for writing the time when the chart movement started changing (up or down) char time_line[]; // Array for storing the data on what line (Yin=0 or Yang=1) direction has changed double time_change_price[]; // Array for writing the chart movement change price double time_central_price[]; // Array for writing the average price during the chart movement change uint a=0; // Variable for building the chart, number of chart reversals is fixed
3.2. Fonction d'initialisation de l'indicateur
La suivante est la fonction d'initialisation de l'indicateur. Les tampons d'indicateurs et leur indexation (principalement sous forme de séries chronologiques ; comme le graphique de Kagi est plus court que le graphique principal, il est préférable de le dessiner à l'envers) y sont spécifiés. De plus, les valeurs qui ne doivent pas être affichées à l'écran sont définies (EMPTY_VALUE=-1).
Maintenant, nous attribuons le nom de l'indicateur et la précision de l'affichage. Comme mentionné précédemment, le nombre magique est ajouté au nom. Ceci est fait pour fournir un fonctionnement correct de la fonction ChartWindowFind(). Sinon, l'objet graphique dessiné dans la fenêtre des indicateurs n'est affiché qu'au premier indicateur lancé (si plusieurs indicateurs sur un même graphique sont utilisés).
Ensuite, nous attribuons des noms aux lignes de construction, interdisons l'affichage des valeurs numériques actuelles dans la fenêtre d'indicateur, définissons la couleur et la largeur des lignes Yin et Yang, définissons le nombre de niveaux de prix affichés dans la fenêtre d'indicateur.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,YinBuffer1,INDICATOR_DATA); ArraySetAsSeries(YinBuffer1,true); SetIndexBuffer(1,YinBuffer2,INDICATOR_DATA); ArraySetAsSeries(YinBuffer2,true); SetIndexBuffer(2,Yin1Buffer,INDICATOR_DATA); ArraySetAsSeries(Yin1Buffer,true); SetIndexBuffer(3,Yin2Buffer,INDICATOR_DATA); ArraySetAsSeries(Yin2Buffer,true); SetIndexBuffer(4,Yin3Buffer,INDICATOR_DATA); ArraySetAsSeries(Yin3Buffer,true); //--- SetIndexBuffer(5,YangBuffer1,INDICATOR_DATA); ArraySetAsSeries(YangBuffer1,true); SetIndexBuffer(6,YangBuffer2,INDICATOR_DATA); ArraySetAsSeries(YangBuffer2,true); SetIndexBuffer(7,Yang1Buffer,INDICATOR_DATA); ArraySetAsSeries(Yang1Buffer,true); SetIndexBuffer(8,Yang2Buffer,INDICATOR_DATA); ArraySetAsSeries(Yang2Buffer,true); SetIndexBuffer(9,Yang3Buffer,INDICATOR_DATA); ArraySetAsSeries(Yang3Buffer,true); //--- add the buffer for copying data on prices for calculation SetIndexBuffer(10,Price,INDICATOR_CALCULATIONS); //--- add the buffer for copying data on bar open time for construction SetIndexBuffer(11,Time,INDICATOR_CALCULATIONS); //--- set what values are not to be drawn for(char x=0; x<8; x++) { PlotIndexSetDouble(x,PLOT_EMPTY_VALUE,-1); } //--- set the indicator's look IndicatorSetString(INDICATOR_SHORTNAME,"BKCV "+IntegerToString(magic_numb)); // Indicator name IndicatorSetInteger(INDICATOR_DIGITS,_Digits); // Display accuracy //--- assign names to graphical constructions PlotIndexSetString(0,PLOT_LABEL,"Yin"); PlotIndexSetString(1,PLOT_LABEL,"Yin"); PlotIndexSetString(2,PLOT_LABEL,"Yin"); PlotIndexSetString(3,PLOT_LABEL,"Yin"); PlotIndexSetString(4,PLOT_LABEL,"Yang"); PlotIndexSetString(5,PLOT_LABEL,"Yang"); PlotIndexSetString(6,PLOT_LABEL,"Yang"); PlotIndexSetString(7,PLOT_LABEL,"Yang"); //--- prohibit display of the results of the current values for graphical constructions PlotIndexSetInteger(0,PLOT_SHOW_DATA,false); PlotIndexSetInteger(1,PLOT_SHOW_DATA,false); PlotIndexSetInteger(2,PLOT_SHOW_DATA,false); PlotIndexSetInteger(3,PLOT_SHOW_DATA,false); PlotIndexSetInteger(4,PLOT_SHOW_DATA,false); PlotIndexSetInteger(5,PLOT_SHOW_DATA,false); PlotIndexSetInteger(6,PLOT_SHOW_DATA,false); PlotIndexSetInteger(7,PLOT_SHOW_DATA,false); //--- set color for Yin line PlotIndexSetInteger(0,PLOT_LINE_COLOR,color_yin); PlotIndexSetInteger(1,PLOT_LINE_COLOR,color_yin); PlotIndexSetInteger(2,PLOT_LINE_COLOR,color_yin); PlotIndexSetInteger(3,PLOT_LINE_COLOR,color_yin); //--- set color for Yang line PlotIndexSetInteger(4,PLOT_LINE_COLOR,color_yang); PlotIndexSetInteger(5,PLOT_LINE_COLOR,color_yang); PlotIndexSetInteger(6,PLOT_LINE_COLOR,color_yang); PlotIndexSetInteger(7,PLOT_LINE_COLOR,color_yang); //--- set Yin line width PlotIndexSetInteger(0,PLOT_LINE_WIDTH,width_yin); PlotIndexSetInteger(1,PLOT_LINE_WIDTH,width_yin); PlotIndexSetInteger(2,PLOT_LINE_WIDTH,width_yin); PlotIndexSetInteger(3,PLOT_LINE_WIDTH,width_yin); //--- set Yang line width PlotIndexSetInteger(4,PLOT_LINE_WIDTH,width_yang); PlotIndexSetInteger(5,PLOT_LINE_WIDTH,width_yang); PlotIndexSetInteger(6,PLOT_LINE_WIDTH,width_yang); PlotIndexSetInteger(7,PLOT_LINE_WIDTH,width_yang); //--- set the number of levels in the indicator window IndicatorSetInteger(INDICATOR_LEVELS,levels_number); //--- return(INIT_SUCCEEDED); }
3.3. Fonction de copie de données
Examinons maintenant les fonctions de copie de données.
Il y en a deux ici. Le premier sert à copier les prix, tandis que le second sert à copier l'heure d'ouverture de chaque barre. Les deux fonctions conservent leurs valeurs dans les tampons de calcul de l'indicateur précédemment déclaré.
Tout d'abord, considérons la fonction de copie des prix. Paramètres d'entrée de la fonction : tableau de stockage des données, heure de début et de fin de la copie des données (heure actuelle). Le corps de la fonction contient les variables pour répondre à la fonction, le nombre de données (barres) copiées dans le tableau intermédiaire, le tableau dynamique intermédiaire lui-même et le nombre de barres qui doivent être copiées dans le tableau intermédiaire. Le nombre de barres est calculé en fonction du nombre total de barres dans la période donnée et du nombre de barres (variable globale) copiées lors de l'appel de fonction précédent.
Si ce n'est pas la première fois que les données sont copiées, les données de la dernière barre copiée doivent être mises à jour. Pour ce faire, nous réduisons le nombre de mesures copiées d'une unité et augmentons d'une unité le nombre de mesures nouvellement copiées. Nous modifions également la taille du tableau intermédiaire en le préparant à copier les barres.
En fonction des paramètres, nous copions les prix dans le tableau intermédiaire. Si la copie réussit, les données sont copiées du tableau intermédiaire jusqu'à la fin du tableau tampon (tableau de réponse de fonction), attribuent la réponse positive à la fonction et mettent à jour la variable globale stockant les données sur le nombre de barres copiées. Ce type de copie permet de ne copier que quelques dernières mesures en réduisant le temps de copie.
//+------------------------------------------------------------------+ //| Func Copy History | //+------------------------------------------------------------------+ bool func_copy_history(double &result_array[], datetime data_start, datetime data_stop) { //--- int x=false; // Variable for answer int result_copy=-1; // Number of copied data static double price_interim[]; // Temporary dynamic array for storing copied data static int bars_to_copy; // Number of bars for copying bars_to_copy=Bars(_Symbol,period,data_start,data_stop); // Find out the current number of bars on the time interval bars_to_copy-=bars_copied; // Calculate the number of bars to be copied if(bars_copied!=0) // If it is not the first time the data has been copied { bars_copied--; bars_to_copy++; } ArrayResize(price_interim,bars_to_copy); // Change the size of the receiving array switch(price_type) { case 0: result_copy=CopyClose(_Symbol,period,0,bars_to_copy,price_interim); break; case 1: result_copy=CopyOpen(_Symbol,period,0,bars_to_copy,price_interim); break; case 2: result_copy=CopyHigh(_Symbol,period,0,bars_to_copy,price_interim); break; case 3: result_copy=CopyLow(_Symbol,period,0,bars_to_copy,price_interim); break; } if(result_copy!=-1) // If copying to the intermediate array is successful { ArrayCopy(result_array,price_interim,bars_copied,0,WHOLE_ARRAY); // Copy the data from the temporary array to the main one x=true; // assign the positive answer to the function bars_copied+=result_copy; // Increase the value of the processed data } //--- return(x); }
La fonction suivante est celle de copier les données de temps. Il est différent du précédent en ce qu'il traite d'un autre type de variable - datetime (qui est converti en double lorsqu'il est copié dans un tableau de tampons temporels - tableau de réponses de fonction). Une autre différence est que l'instruction switch() n'est pas utilisée, car il n'est pas nécessaire de sélectionner les données copiées.
//+------------------------------------------------------------------+ //| Func Copy Time | //+------------------------------------------------------------------+ bool func_copy_time(double &result_array[], datetime data_start, datetime data_stop) { //--- int x=false; // Variable for answer int result_copy=-1; // Number of copied data static datetime time_interim[]; // Temporary dynamic array for storing copied data static int bars_to_copy_time; // Number of bars for copying bars_to_copy_time=Bars(_Symbol,period,data_start,data_stop); // Find out the current number of bars on the time interval bars_to_copy_time-=bars_copied_time; // Calculate the number of bars to be copied if(bars_copied_time!=0) // If it is not the first time the data has been copied { bars_copied_time--; bars_to_copy_time++; } ArrayResize(time_interim,bars_to_copy_time); // Change the size of the receiving array result_copy=CopyTime(_Symbol,period,0,bars_to_copy_time,time_interim); if(result_copy!=-1) // If copying to the intermediate array is successful { ArrayCopy(result_array,time_interim,bars_copied_time,0,WHOLE_ARRAY); // Copy the data from the temporary array to the main one x=true; // assign the positive answer to the function bars_copied_time+=result_copy; // Increase the value of the processed data } //--- return(x); }
3.4. Fonction de calcul du paramètre de retournement
Étant donné que le paramètre de retournement peut être un point ou un pourcentage, nous avons besoin de la fonction qui calculera le paramètre de retournement en fonction des paramètres de l'indicateur. La fonction n'a qu'un seul paramètre - le prix pour calculer le pourcentage de retournement. La variable pour la réponse est d'abord initialisée par type double et après les calculs, elle est indirectement convertie en type int pour la réponse.
Cela est dû au fait que des nombres à virgule flottante sont utilisés dans les calculs, tandis que la réponse doit être présentée sous forme d'entiers. La sélection est implémentée dans la fonction par l'instruction conditionnelle if-else. La comparaison est effectuée directement avec une variable d'entrée externe (paramètres indicateurs). Le calcul des points est effectué à l'aide d'une équation simple. Tout d'abord, le nombre total de points que le prix a dépassé est défini. Ensuite, le pourcentage spécifié est calculé en fonction de ce nombre et affecté à la variable renvoyée.
//+------------------------------------------------------------------+ //| Func Calculate Doorstep | //+------------------------------------------------------------------+ int func_calc_dorstep(double price) { double x=0; // Variable for answer if(type_doorstep==0) // If the calculation is to be performed in points { x=doorstep; } if(type_doorstep==1) // If the calculation is to be performed in percentage { x=price/_Point*doorstep/100; } return((int)x); }
3.5. La fonction principale - Dessin du graphique de Kagi
Nous avons déjà examiné toutes les fonctions nécessaires au fonctionnement de la fonction principale - dessin du graphique de Kagi dans la fenêtre de l'indicateur (c'est-à-dire remplir les tampons de l'indicateur). Les paramètres d'entrée de la fonction sont constitués de tableaux de données. Deux d'entre eux sont les tampons de calcul décrits ci-dessus (précédemment copiés Prix et Temps), tous les autres sont les tableaux des tampons de construction graphique de l'indicateur.
Les variables nécessaires au stockage des données sur la construction du graphique sont déclarées à l'intérieur de la fonction. Étant donné que le graphique est construit en utilisant l'instruction de boucle for, nous devrions avoir les données sur l'étape à laquelle la passe précédente s'est terminée. Cela peut être réalisé par six variables : line_move - où le prix a bougé au passage précédent, line_gauge - calibre de ligne (largeur de ligne) - Yin ou Yang, price_1 et price_2 - prix précédent et actuel considéré, price_down et price_up - prix précédent de une épaule et une taille. Comme on peut le voir, price_1 est immédiatement assimilé au premier élément du tableau des prix copiés du fait que cette variable est impliquée dans les calculs avant comparaison dès le début de la boucle.
Étant donné que les tableaux de tampons de la construction graphique de l'indicateur ont un indicateur d'indexation AS_SERIES, ils doivent être remplis dans l'ordre inverse. Pour y parvenir, des tableaux temporels ayant la taille appropriée sont implémentés. Les variables globales de stockage des données sur le temps, les types de lignes, "épaule" et "taille", ainsi que les prix de retournement sont ensuite convertis de la même manière.
Ensuite, tous les tableaux doivent être remplis de valeurs "vides" (-1). Cela se fait à l'aide de deux petites boucles. Il est possible de tout joindre en une seule boucle. Mais en utiliser deux rend toutes les actions effectuées beaucoup plus claires, tandis que le temps d'exécution ne change pas beaucoup. En d'autres termes, les tampons graphiques et les tableaux de temps de calcul sont remplis séparément.
Maintenant, toutes les variables sont déclarées, converties et remplies, afin que la boucle principale puisse être lancée. Il est assez volumineux (bien que le calcul soit effectué assez rapidement) et inclut l'analyse de toutes les barres précédemment copiées.
La boucle passe par toutes les barres copiées et remplit les tableaux préalablement déclarés nécessaires pour continuer à les utiliser. Tout d'abord, définissons tous les tableaux utilisés dans la boucle :
- yin_int_1 - valeur principale du prix de la ligne Yin verticale (si la ligne Yin verticale est dessinée et que le graphique se déplace vers le bas, il s'agit de la valeur supérieure du prix ; si le graphique se déplace vers le haut, nous avons le cas inverse);
- yin_int_2 - valeur secondaire du prix de la ligne Yin verticale (si la ligne ascendante est tracée, c'est la valeur supérieure ; si la ligne est descendante, nous avons le cas inverse);
- yang_int_1 - valeur principale du prix de la ligne Yang verticale ;
- yang_int_2 - valeur secondaire du prix de la ligne Yang verticale ;
- lin_yin - valeur de la ligne Yin horizontale (prix de retournement à la ligne Yin) ;
- lin_yang - valeur de la ligne Yang horizontale (prix de retournement à la ligne Yang) ;
- time_change - heure de retournement de graphique (construction d'une épaule ou d'une taille) ;
- time_line - la ligne pendant le retournement Yin = 0 ou Yang = 1 ;
- time_central_price - la valeur du prix central, le prix au moment où la ligne Yin se transforme en Yang ou vice versa ;
- time_change_price - la valeur du prix de retournement (épaule ou taille), la variable est commune et ne dépend pas des types de lignes Yin ou Yang.
La valeur du prix actuellement analysé à partir du tampon de prix est affectée à la variable price_2 avant chaque passage de boucle pour une comparaison ultérieure dans les instructions conditionnelles if-else. Après cela, le tableau tampon des données copiées est analysé étape par étape et les tableaux mentionnés ci-dessus sont remplis. Chaque instruction conditionnelle if-else exécute certaines actions en fonction des conditions : direction précédente des lignes du graphique (haut ou bas) et aspect précédent des lignes (Yin ou Yang). Ensuite, les conditions de circulation (que le prix ait dépassé un certain nombre de points) sont vérifiées selon le type de construction (standard ou modifié).
Si tout va bien, de nouvelles variables (éléments de tableau) sont réaffectées ou définies. Le type de ligne (Yin ou Yang) est défini au tout début. En fonction du mouvement et des actions précédentes, la distribution ultérieure est effectuée.
Il y a deux mouvements de prix possibles :
- Le prix monte;
- Le prix baisse.
Il y a aussi quatre types d'actions précédentes dans chaque direction :
- La ligne précédente était Yin et elle s'est déplacée vers le haut ;
- La ligne précédente était Yang et elle s'est déplacée vers le haut ;
- La ligne précédente était Yin et elle s'est déplacée vers le bas ;
- La ligne précédente était Yang et elle s'est déplacée vers le bas.
Ainsi, nous avons huit cas en dehors des deux premières définitions du mouvement initial du graphique (apparition de la première ligne).
Après cela, la boucle principale est terminée. La réaffectation (retournement) et le remplissage des tampons sont effectués pour construire le graphe dans une boucle plus petite constituée du nombre de retournements du graphe de Kagi préalablement défini dans la boucle principale et écrit dans la variable “a”. Quant à la distribution des valeurs de prix supérieures et inférieures et des lignes verticales, tout est assez simple : un simple retournement est effectué. En d'autres termes, les valeurs primaires obtenues précédemment (tableaux ayant les indices 0,1,2,3...) sont affectées aux valeurs finales des tampons (l'élément ayant l'indice "а", c'est-à-dire а,а-1,а-2,а-3... est utilisé comme valeur finale). Pour empêcher les lignes de retournement (horizontales) de coller ensemble, la rotation à l'aide de l'instruction switch est effectuée comme mentionné ci-dessus.
À cela, le travail de la fonction principale de construction du graphique Kagi est terminé.
//+------------------------------------------------------------------+ //| Func Draw Kagi | //+------------------------------------------------------------------+ void func_draw_kagi(double &array_input[], double &arr_yin_1[], double &arr_yin_2[], double &arr_yin_lin1[], double &arr_yin_lin2[], double &arr_yin_lin3[], double &arr_yang_1[], double &arr_yang_2[], double &arr_yang_lin1[], double &arr_yang_lin2[], double &arr_yang_lin3[], double &arr_time[]) { //--- a=0; // Variable for the chart construction fixing the number of chart reversals char line_move=0; // Previous price direction 1-up, -1-down char line_gauge=0; // Previous look of the line 1-thick yang, -1-thin yin double price_1=0,price_2=0; // Auxiliary variables for defining the price movement double price_down=-99999,price_up=99999; // Auxiliary variables for storing the reversal price values price_1=array_input[0]; //--- auxiliary arrays for the initial data storing before the reversal (transferring to the buffers) double yin_int_1[]; double yin_int_2[]; double lin_yin[]; double yang_int_1[]; double yang_int_2[]; double lin_yang[]; //--- change the sizes of dynamic arrays ArrayResize(yin_int_1,bars_copied); ArrayResize(yin_int_2,bars_copied); ArrayResize(yang_int_1,bars_copied); ArrayResize(yang_int_2,bars_copied); ArrayResize(lin_yin,bars_copied); ArrayResize(lin_yang,bars_copied); //--- time data storing arrays ArrayResize(time_change,bars_copied_time); ArrayResize(time_line,bars_copied_time); // Look of the line Yin = 0 or Yang = 1 ArrayResize(time_change_price,bars_copied_time); ArrayResize(time_central_price,bars_copied_time); //--- assign -1 (not displayed) value to the transferred buffers for(int z=0; z<bars_copied; z++) { arr_yin_1[z]=-1; arr_yin_2[z]=-1; arr_yin_lin1[z]=-1; arr_yin_lin2[z]=-1; arr_yin_lin3[z]=-1; arr_yang_1[z]=-1; arr_yang_2[z]=-1; arr_yang_lin1[z]=-1; arr_yang_lin2[z]=-1; arr_yang_lin3[z]=-1; } //--- equate -1 (not displayed) value to the arrays for(int z=0; z<bars_copied; z++) { yin_int_1[z]=-1; yin_int_2[z]=-1; lin_yin[z]=-1; yang_int_1[z]=-1; yang_int_2[z]=-1; lin_yang[z]=-1; time_change[z]=-1; time_line[z]=-1; time_change_price[z]=-1; time_central_price[z]=-1; } //--- function's main loop for(int z=0; z<bars_copied; z++) { price_2=array_input[z]; //--- first, let's define the initial market direction //--- first THIN DESCENDING line if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) && line_move==0) { yin_int_1[a]=price_1; yin_int_2[a]=price_2; line_move=-1; line_gauge=-1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; } //--- first THICK ASCENDING line if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) && line_move==0) { yang_int_1[a]=price_1; yang_int_2[a]=price_2; line_move=1; line_gauge=1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; } //--- price moves DOWN //--- if the price moved DOWN before that, the line is THIN if(line_move==-1 && line_gauge==-1) { if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point>0)) { yin_int_2[a]=price_2; line_move=-1; line_gauge=-1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; } } //--- if the price moved DOWN before that, the line is THICK if(line_move==-1 && line_gauge==1) { if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point>0)) { if(price_2<price_down) // If the thick line crossed the lower shoulder when moving downwards { yin_int_1[a]=price_down; yin_int_2[a]=price_2; yang_int_2[a]=price_down; line_move=-1; line_gauge=-1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_central_price[a]=price_down; time_line[a]=0; } else //if(price_2>=price_down) // If the thick line has not crossed the lower shoulder when moving downwards { yang_int_2[a]=price_2; line_move=-1; line_gauge=1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; } } } //--- if the price has moved UPWARDS before that, the line is THIN if(line_move==1 && line_gauge==-1) { if((price_1-price_2)/_Point>func_calc_dorstep(price_2)) { a++; yin_int_1[a]=price_1; yin_int_2[a]=price_2; lin_yin[a]=price_1; line_move=-1; line_gauge=-1; price_up=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; time_change_price[a]=lin_yin[a]; } } //--- if the price has moved UPWARDS before that, the line is THICK if(line_move==1 && line_gauge==1) { if((price_1-price_2)/_Point>func_calc_dorstep(price_2)) { a++; if(price_2<price_down) // If the thick line has crossed the lower shoulder when moving downwards { yin_int_1[a]=price_down; yin_int_2[a]=price_2; yang_int_1[a]=price_1; yang_int_2[a]=price_down; lin_yang[a]=price_1; line_move=-1; line_gauge=-1; price_up=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; time_change_price[a]=lin_yang[a]; time_central_price[a]=price_down; } else//if(price_2>=price_down) // If the thick line has not crossed the lower shoulder when moving downwards { yang_int_1[a]=price_1; yang_int_2[a]=price_2; lin_yang[a]=price_1; line_move=-1; line_gauge=1; price_up=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; time_change_price[a]=lin_yang[a]; } } } //--- the price moves UP //--- if the price has moved UPWARDS before that, the line is THICK if(line_move==1 && line_gauge==1) { if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point<0)) { yang_int_2[a]=price_2; line_move=1; line_gauge=1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; } } //--- if the price has moved UPWARDS before that, the line is THIN if(line_move==1 && line_gauge==-1) { if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point<0)) { if(price_2>price_up) // If the thin line has not crossed the upper shoulder when moving upwards { yin_int_2[a]=price_up; yang_int_1[a]=price_up; yang_int_2[a]=price_2; line_move=1; line_gauge=1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_central_price[a]=price_up; time_line[a]=1; } else//if(price_2<=price_up) // If the thin line has not crossed the upper shoulder when moving upwards { yin_int_2[a]=price_2; line_move=1; line_gauge=-1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; } } } //--- if the price has moved DOWNWARDS before that, the line is THICK if(line_move==-1 && line_gauge==1) { if((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) { a++; yang_int_1[a]=price_1; yang_int_2[a]=price_2; lin_yang[a]=price_1; line_move=1; line_gauge=1; price_down=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; time_change_price[a]=lin_yang[a]; } } //--- if the price has moved DOWNWARDS before that, the line is THIN if(line_move==-1 && line_gauge==-1) { if((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) { a++; if(price_2>price_up) // If the thin line has crossed the upper shoulder when moving upwards { yin_int_1[a]=price_1; yin_int_2[a]=price_up; yang_int_1[a]=price_up; yang_int_2[a]=price_2; lin_yin[a]=price_1; line_move=1; line_gauge=1; price_down=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=1; time_change_price[a]=lin_yin[a]; time_central_price[a]=price_up; } else //if(price_2<=price_up) // If the thin line has not crossed the upper shoulder when moving upwards { yin_int_1[a]=price_1; yin_int_2[a]=price_2; lin_yin[a]=price_1; line_move=1; line_gauge=-1; price_down=price_1; price_1=price_2; time_change[a]=(datetime)arr_time[z]; time_line[a]=0; time_change_price[a]=lin_yin[a]; } } } } //--- function's main loop //--- assign actual values to drawing buffers uint y=a; //--- auxiliary variables for storing data on filling the current buffer char yin=1; char yang=1; for(uint z=0; z<=a; z++) { arr_yin_1[z]=yin_int_1[y]; arr_yin_2[z]=yin_int_2[y]; switch(yin) { case 1: { arr_yin_lin1[z]=lin_yin[y]; arr_yin_lin1[z+1]=lin_yin[y]; yin++; } break; case 2: { arr_yin_lin2[z]=lin_yin[y]; arr_yin_lin2[z+1]=lin_yin[y]; yin++; } break; case 3: { arr_yin_lin3[z]=lin_yin[y]; arr_yin_lin3[z+1]=lin_yin[y]; yin=1; } break; } arr_yang_1[z]=yang_int_1[y]; arr_yang_2[z]=yang_int_2[y]; switch(yang) { case 1: { arr_yang_lin1[z]=lin_yang[y]; arr_yang_lin1[z+1]=lin_yang[y]; yang++; } break; case 2: { arr_yang_lin2[z]=lin_yang[y]; arr_yang_lin2[z+1]=lin_yang[y]; yang++; } break; case 3: { arr_yang_lin3[z]=lin_yang[y]; arr_yang_lin3[z+1]=lin_yang[y]; yang=1; } break; } y--; } //--- }
3.6. Fonction de création d'un objet graphique "Ligne de tendance"
Examinons maintenant la fonction de création d'un objet graphique "ligne de tendance". Cette fonction est nécessaire pour dessiner Kagi sur le graphique principal.
La fonction est très simple. Il contient les paramètres d'entrée nécessaires à la création de l'objet graphique "ligne de tendance" : nom de l'objet, premier et deuxième prix et points de temps, ainsi que la largeur et la couleur de la ligne. Le corps de la fonction contient la fonction de création d'objet graphique et six fonctions de modification des propriétés de l'objet graphique.
//+------------------------------------------------------------------+ //| Func Object Create Trend Line | //+------------------------------------------------------------------+ void func_create_trend_line(string name, double price1, double price2, datetime time1, datetime time2, int width, color color_line) { ObjectCreate(0,name,OBJ_TREND,0,time1,price1,time2,price2); //--- set the line color ObjectSetInteger(0,name,OBJPROP_COLOR,color_line); //--- set the line display style ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID); //--- set the line width ObjectSetInteger(0,name,OBJPROP_WIDTH,width); //--- display in the foreground (false) or background (true) ObjectSetInteger(0,name,OBJPROP_BACK,false); //--- enable (true) or disable (false) the mode of continuing the line display to the left ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false); //--- enable (true) or disable (false) the mode of continuing the line display to the right ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false); }
3.7. Dessin de Kagi sur le graphique principal
La fonction suivante qui s'applique plusieurs fois à la précédente est la fonction de construction Kagi sur le graphique principal. Les variables globales remplies dans la fonction principale précédemment examinée de la construction du graphique de Kagi sont utilisées comme variables d'entrée : le tableau des prix de retournement ("épaules" et "taille"), le tableau de changement et les prix centraux (le prix auquel la ligne Yin se transforme en Yang ou vice versa), tableau de temps de retournement (situé en temps réel, l'indice de tableau [z-1] est utilisé pour marquer le début de retournement), le tableau du type de la ligne, à laquelle le retournement s'est produite (il est également un élément en avant, comme le tableau de temps).
Le corps de la fonction consiste en une boucle. La boucle est divisée en deux parties : dessin des lignes verticales et horizontales. Le premier est également divisé en deux : dessin des verticales compte tenu du changement de ligne (changement de prix central) et de l'absence de changement. Notez les paramètres transférés de la fonction de création d'objet "ligne de tendance".
Le nommage est effectué à plusieurs reprises. Le nom de l'objet commence par un nombre magique (nécessaire pour supprimer les objets d'un certain indicateur), puis son type est fixé et enfin l'index est attribué. L'index est mis à jour à chaque passage de la boucle.
//+------------------------------------------------------------------+ //| Func Kagi Main Chart | //+------------------------------------------------------------------+ void func_kagi_main_chart(double &price[], // Shoulder prices array double ¢ral_price[], // Array of the prices of passing through the shoulders datetime &time[], // Current location time array ([-1] - start of shoulder) char &type_line_end[]) // Line type by the start of shoulder formation { //--- start of the loop for(uint z=1; z<=a; z++) { //--- check for the pass conditions (no pass) if(central_price[z]==-1) { if(type_line_end[z-1]==0 && price[z+1]!=-1) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z), price[z],price[z+1],time[z],time[z],width_yin_main,color_yin_main); } if(type_line_end[z-1]==1 && price[z+1]!=-1) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z), price[z],price[z+1],time[z],time[z],width_yang_main,color_yang_main); } } else //--- check for the pass conditions (pass is present) { if(type_line_end[z-1]==0 && price[z+1]!=-1) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z), central_price[z],price[z],time[z],time[z],width_yin_main,color_yin_main); func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z), central_price[z],price[z+1],time[z],time[z],width_yang_main,color_yang_main); } if(type_line_end[z-1]==1 && price[z+1]!=-1) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z), central_price[z],price[z+1],time[z],time[z],width_yin_main,color_yin_main); func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z), central_price[z],price[z],time[z],time[z],width_yang_main,color_yang_main); } } //--- check for the pass conditions (pass is present) //--- draw the horizontals if(type_line_end[z-1]==0) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_h"+IntegerToString(z), price[z],price[z],time[z-1],time[z],width_yin_main,color_yin_main); } if(type_line_end[z-1]==1) { func_create_trend_line(IntegerToString(magic_numb)+"_trend_h"+IntegerToString(z), price[z],price[z],time[z-1],time[z],width_yang_main,color_yang_main); } //--- draw the horizontals } }
3.8. Implémentation d'étiquettes supplémentaires
Comme je l'ai déjà mentionné ci-dessus, l'indicateur implémente des étiquettes supplémentaires. Examinons la fonction fournissant ces étiquettes sur le graphique principal. Il n'y a que deux types d'étiquettes ici : les étiquettes de prix de retournement et de temps de retournement affichées via "étiquette de prix" et "étiquette verticale". Les paramètres suivants sont passés en entrée : attribut du dessin de l'étiquette de prix de retournement et de la couleur de l'étiquette, attributs du dessin de l'étiquette de l'heure de retournement et du changement de couleur de l'étiquette, les première et deuxième couleurs de l'heure de retournement.
L'ensemble de la fonction est divisé en deux parties : la première partie est responsable des étiquettes de temps, tandis que la seconde - des étiquettes de prix. Les deux parties de la fonction sont constituées des boucles limitées par le nombre de retournements de graphiques (variable “a”). L'instruction conditionnelle if-else est définie avant la boucle. La déclaration vérifie la nécessité de leur dessin en fonction des paramètres de l'indicateur.
La première boucle crée des étiquettes temporelles, la définition du nom de l'objet est effectuée au début de la boucle (le principe de génération de nom a été décrit ci-dessus). Ensuite, la couleur est sélectionnée en fonction de la ligne du tableau de type de ligne déclaré globalement (si le paramètre est défini) et d'autres paramètres sont appliqués à la ligne.
La deuxième boucle est chargée de créer des étiquettes de prix de retournement. Tout d'abord, le nom de l'objet est généré. Ensuite, la sélection de l'index du tableau temporel est définie selon que Kagi doit être construit sur le graphique principal ou non. si cela n'est pas fait, les étiquettes seront situées "en l'air" et on ne verra pas assez à partir de quel endroit le retournement s'est produit. Ensuite, l'objet de type "étiquette de prix" est créé et configuré.
//+------------------------------------------------------------------+ //| Func Label Main Chart | //+------------------------------------------------------------------+ void func_label_main_chart(bool label_print, color label_color, bool time_change_print, bool time_change_color, color time_color_first, color time_color_second) { if(time_change_print==true) { for(uint z=1; z<=a; z++) { string name=IntegerToString(magic_numb)+"_time_2_"+IntegerToString(z); //--- create an object of a vertical line type ObjectCreate(0,name,OBJ_VLINE,0,time_change[z],0); //--- set the line color color color_line=clrBlack; if(time_change_color==true) { if(time_line[z]==0)color_line=time_color_first; if(time_line[z]==1)color_line=time_color_second; } else color_line=time_color_first; ObjectSetInteger(0,name,OBJPROP_COLOR,color_line); //--- set the line display style ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID); //--- set the line width ObjectSetInteger(0,name,OBJPROP_WIDTH,1); //--- display on the foreground (false) or background (true) ObjectSetInteger(0,name,OBJPROP_BACK,false); //--- enable (true) or disable (false) the line display mode in the chart subwindows ObjectSetInteger(0,name,OBJPROP_RAY,time_separate_windows); } } if(label_print==true) { for(uint z=1; z<=a; z++) { string name=IntegerToString(magic_numb)+"_label_2_"+IntegerToString(z); uint numb_time; if(kagi_main_chart==true)numb_time=z; else numb_time=z-1; //--- create a label type object ObjectCreate(0,name,OBJ_ARROW_RIGHT_PRICE,0,time_change[numb_time],time_change_price[z]); //--- set the label color ObjectSetInteger(0,name,OBJPROP_COLOR,label_color); //--- set the edging line style ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID); //--- set the label size ObjectSetInteger(0,name,OBJPROP_WIDTH,1); //--- display on the foreground (false) or background (true) ObjectSetInteger(0,name,OBJPROP_BACK,false); } } }
Voyons maintenant comment nous pouvons définir les étiquettes dans la fenêtre d'indicateur.
Toutes les étiquettes de la fenêtre d'indicateur sont principalement des étiquettes de prix, et il n'y en a que deux types : les étiquettes de prix de retournement et les niveaux de prix. Il existe deux types de tracé des niveaux de prix : sur les retournement du graphique et à égale distance de toute la fourchette de prix du graphique. Le premier type peut changer la couleur des niveaux de deux manières : selon le type de ligne (Yin ou Yang) et selon le retournement (haut ou bas).
Ainsi, la fonction elle-même est divisée en deux boucles : la première est chargée de créer des étiquettes de prix de retournement, la seconde s'occupe de la désignation des niveaux de prix. Ce dernier est encore divisé en deux types : étiquette à chaque retournement ou étiquettes sur toute la gamme de prix à un niveau égal.
Cette fonction est différente de la précédente en ce qu'elle a des limitations sur le nombre d'étiquettes de prix et de niveaux en raison du fait qu'elles surchargent en grand nombre le graphique ce qui complique sa compréhension.
En raison de cette fonctionnalité, les deux boucles sont limitées par le nombre de passes spécifié dans les paramètres de l'indicateur (nombre d'étiquettes de prix et de niveaux). Une telle approche est dangereuse car le nombre de retournement peut s'avérer bien inférieur au nombre d'étiquettes de prix définies dans les paramètres. Pour cette raison, la présence de retournement lors de chaque passage en boucle est vérifiée pour dessiner une étiquette de prix ou un niveau.
La seule exception consiste à tracer les niveaux de prix sur toute la gamme de prix sur une distance égale. La génération d'objets graphiques de type "Étiquette de prix" s'effectue dans les coordonnées dans l'ordre inverse, c'est-à-dire que les étiquettes sont placées de la date actuelle au passé. Il en va de même pour les niveaux de prix : les niveaux de prix actuels sont générés en premier, suivis des précédents. Les exceptions sont les niveaux de prix qui ne dépendent pas des retournements du graphique.
Les changements de couleur au niveau des prix sont effectués à l'aide d'instructions conditionnelles if-else en fonction des paramètres.
//+------------------------------------------------------------------+ //| Func Label Indicator Window | //+------------------------------------------------------------------+ void func_label_indicator_window(bool label_print, // Draw price labels bool levels_print, // Draw levels char levels_type_draw, // Type of drawing the levels by reversals or at an equal distance of the entire price range char levels_color_change) // Change line color { uint number=a; if(label_print==true) { for(uint z=0; z<=label_1_number; z++) { if(z<number) { string name=IntegerToString(magic_numb)+"_label_1_"+IntegerToString(z); //--- create label type object ObjectCreate(0,name,OBJ_ARROW_RIGHT_PRICE,ChartWindowFind(),(datetime)Time[(bars_copied_time-z-2)],time_change_price[number-z]); //--- set the label color ObjectSetInteger(0,name,OBJPROP_COLOR,label_1_color); //--- set the style of the edging line ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID); //--- set the label size ObjectSetInteger(0,name,OBJPROP_WIDTH,1); //--- display on the foreground (false) or background (true) ObjectSetInteger(0,name,OBJPROP_BACK,false); } } } if(levels_print==true) { if(levels_type_draw==0) { for(uint z=0; z<=levels_number; z++) { if(z<number) { IndicatorSetDouble(INDICATOR_LEVELVALUE,z,time_change_price[number-z]); if(levels_change_color==0) { double numb_even=z; if(MathMod(numb_even,2)==0) { IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color); } if(MathMod(numb_even,2)!=0) { IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_second_color); } } if(levels_change_color==1) { if(time_line[number-z]==0)IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color); if(time_line[number-z]==1)IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_second_color); } if(levels_change_color==2) { IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color); } } } } if(levels_type_draw==1) { double max_price=Price[ArrayMaximum(Price)]; double min_price=Price[ArrayMinimum(Price,1,ArrayMinimum(Price)-1)]; double number_difference=(max_price-min_price)/levels_number; NormalizeDouble(number_difference,_Digits); for(uint z=0; z<=levels_number; z++) { IndicatorSetDouble(INDICATOR_LEVELVALUE,z,(min_price+(z*number_difference))); IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color); } } } }
3.9. Suppression d'objets graphiques créés précédemment
On sait déjà que cet indicateur est riche en objets graphiques. Il est temps de réfléchir à la manière dont nous pouvons les supprimer rapidement et efficacement.
Cette tâche est exécutée par la fonction de suppression des objets graphiques. Le nom initial et le nombre d'objets sont utilisés comme paramètres de fonction. Comme lors de la création, le nom de l'objet doit contenir le nombre magique et le nom d'un type d'objet. L'appel de fonction dans le programme est limité par le nombre d'objets dépassant leur existence possible. Cependant, cela n'affecte pas la fonctionnalité de l'indicateur.
//+------------------------------------------------------------------+ //| Func Delete Objects | //+------------------------------------------------------------------+ void func_delete_objects(string name, int number) { string name_del; for(int x=0; x<=number; x++) { name_del=name+IntegerToString(x); ObjectDelete(0,name_del); } }
3.10. Fonction de lancement de la construction du graphique
Maintenant, après avoir examiné toutes les fonctions de calcul et de construction du graphique de Kagi, ainsi que de création et de suppression d'objets, nous devrions envisager une autre petite fonction pour vérifier l'arrivée de la nouvelle barre. La fonction est assez simple et a un paramètre d'entrée - la période analysée. La réponse de la fonction est également très simple. Il est de type bool et contient la réponse indiquant si une nouvelle barre est présente ou non. La base du corps de la fonction est l'instruction switch qui passe la commande à ses différentes instructions en fonction de la période.
Dans l'exemple, la fonction couvre toute la plage de périodes, bien qu'une seule période puisse également être utilisée.
L'algorithme de la fonction a été repris du code IsNewBar : l'heure de la dernière ouverture de barre est comparée à la valeur de temps précédemment définie. Si les valeurs sont différentes, il y a une nouvelle barre. La nouvelle valeur est attribuée comme précédemment définie et la réponse de la fonction est considérée comme positive. Si l'heure d'ouverture de la dernière barre coïncide avec la valeur temporelle précédemment déterminée, alors la nouvelle barre n'est pas encore apparue et la réponse de la fonction est négative.
//+------------------------------------------------------------------+ //| Func New Bar | //+------------------------------------------------------------------+ bool func_new_bar(ENUM_TIMEFRAMES period_time) { //---- static datetime old_Times[22];// array for storing old values bool res=false; // analysis result variable int i=0; // old_Times[] array cell index datetime new_Time[1]; // new bar time switch(period_time) { case PERIOD_M1: i= 0; break; case PERIOD_M2: i= 1; break; case PERIOD_M3: i= 2; break; case PERIOD_M4: i= 3; break; case PERIOD_M5: i= 4; break; case PERIOD_M6: i= 5; break; case PERIOD_M10: i= 6; break; case PERIOD_M12: i= 7; break; case PERIOD_M15: i= 8; break; case PERIOD_M20: i= 9; break; case PERIOD_M30: i=10; break; case PERIOD_H1: i=11; break; case PERIOD_H2: i=12; break; case PERIOD_H3: i=13; break; case PERIOD_H4: i=14; break; case PERIOD_H6: i=15; break; case PERIOD_H8: i=16; break; case PERIOD_H12: i=17; break; case PERIOD_D1: i=18; break; case PERIOD_W1: i=19; break; case PERIOD_MN1: i=20; break; case PERIOD_CURRENT: i=21; break; } // copy the time of the last bar to new_Time[0] cell int copied=CopyTime(_Symbol,period_time,0,1,new_Time); if(copied>0) // all is well. Data has been copied { if(old_Times[i]!=new_Time[0]) // if the bar's old time is not equal to new one { if(old_Times[i]!=0) res=true; // if it is not the first launch, true = new bar old_Times[i]=new_Time[0]; // store the bar's time } } //---- return(res); }
3.11. Fonctions OnCalculate() et OnChartEvent()
Toutes les fonctions décrites ci-dessus sont regroupées dans la fonction du même nom – Func Consolidation. Cette fonction est lancée à chaque fois qu'une nouvelle barre apparaît dans la fonction OnCalculate() et lorsque la touche "R" est enfoncée depuis la fonction OnChartEvent().
Avant la génération ou la mise à jour du graphe, la fonction de suppression de tous les objets graphiques est appelée dans la fonction de consolidation (Func Consolidation). Comme il y a beaucoup d'objets et qu'ils sont divisés en étiquettes de prix du graphique principal et de la fenêtre d'indicateur, des lignes verticales indiquant le temps de retournement, ainsi que des lignes de tendance verticales et horizontales Yin et Yang, le nombre général d'appels de fonction est de 7.
Ensuite, les données d'historique sont copiées par prix et heure. La fonction principale de construction du graphique Kagi est lancée par la suite. Après cela, la fonction permettant de placer toutes les étiquettes de prix sur le graphique principal et la fenêtre d'indicateur est appelée. Enfin, Kagi est généré sur le graphique principal et la fonction de redessiner les objets est lancée.
//+------------------------------------------------------------------+ //| Func Consolidation | //+------------------------------------------------------------------+ void func_consolidation() { //--- date of construction end stop_data=TimeCurrent(); //--- deleting all graphical objects belonging to the indicator func_delete_objects(IntegerToString(magic_numb)+"_label_2_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_label_1_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_time_2_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_yin_v",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_yang_v",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1)); //--- copy price data to the main buffer copy_history=func_copy_history(Price,start_data,stop_data); //--- display information about the error when copying price data if(copy_history==false)Alert("Error of copy history Price"); //--- copy time data to the main buffer copy_time=func_copy_time(Time,start_data,stop_data); //--- display a notification of the error occurred while copying time data if(copy_time==false)Alert("Error of copy history Time"); //--- construct Kagi chart in the indicator window func_draw_kagi(Price,YinBuffer1,YinBuffer2,Yin1Buffer,Yin2Buffer,Yin3Buffer, YangBuffer1,YangBuffer2,Yang1Buffer,Yang2Buffer,Yang3Buffer,Time); //--- draw labels on the main chart func_label_main_chart(label_2,label_2_color,time_line_draw,time_line_change_color,time_first_color,time_second_color); //--- draw labels on the indicator chart func_label_indicator_window(label_1,levels_on_off,levels_type,levels_change_color); //--- construct Kagi chart in the main window if(kagi_main_chart==true)func_kagi_main_chart(time_change_price,time_central_price,time_change,time_line); //--- redraw the chart ChartRedraw(0); //--- } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- if(func_new_bar(period_to_redraw)==true) { func_consolidation(); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| OnChartEvent | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // event ID const long& lparam, // long type event parameter const double& dparam, // double type event parameter const string& sparam) // string type event parameter { if(id==CHARTEVENT_KEYDOWN) // Keyboard button pressing event { if(lparam==82) // "R" key has been pressed { func_consolidation(); } } }
3.12. Fonction OnDeinit()
La suppression de tous les objets est effectuée dans la fonction de désinitialisation de l'indicateur.
//+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- delete all graphical objects belonging to the indicator func_delete_objects(IntegerToString(magic_numb)+"_label_2_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_label_1_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_time_2_",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_yin_v",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_yang_v",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1)); func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1)); //--- redraw the chart ChartRedraw(0); }
Passons maintenant à l'utilisation de l'indicateur dans la pratique.
4. Utilisation du graphique Kagi en pratique
Il existe de nombreuses stratégies de trading basées sur le graphique Kagi. Nous allons en examiner quelques-uns.
Commençons par la stratégie la plus populaire : vendre lorsque le Yang se transforme en Yin et acheter dans le cas contraire. Ceci est montré dans la Fig. 4:
Fig. 4. Vendre quand Yang se change en Yin et acheter dans le cas contraire
Comme le montre la figure 4 (EURUSD M30, 5 points), cette stratégie affiche de bons résultats. Le chiffre affiche 8 points pour 4 signaux, le premier (1) montre que la position longue devrait être ouverte à 1,3518, ce qui semble correct puisque le prix atteint alors environ 1,3560 soit 42 points par jour. C'est un bon résultat.
Le point suivant (2) recommande de vendre à 1,3519. Comme nous pouvons le voir, le prix baisse en franchissant le niveau de 1,3485 (et couvrant 34 points) pendant environ deux heures.
Passons au point (3). La position longue est ouverte à 1,3538 et le prix monte pour atteindre 1,3695. Ainsi, le bénéfice comprend déjà 157 points pour un jour et demi. Bien sûr, ce sont les profits les plus élevés possibles mais le résultat est quand même assez bon.
La prochaine stratégie de trading recule par rapport à la ligne de tendance illustrée à la figure 5 (EURUSD M30, 5 points), du 7 au 18 octobre :
Fig. 5. Revenir en arrière de la ligne de tendance
Nous pouvons aller plus loin et trader en suivant les canaux. Un exemple de recherche de canal peut être examiné sur la figure 6 (EURUSD H1, 5 points), à peu près à la même période :
Fig. 6. Trading par canaux
La stratégie la moins populaire basée sur le fait qu'après 7-10 augmentations successives des "épaules" ou diminutions des "tailles", il y aura certainement un retournement (chute ou hausse).
Ceci est illustré dans la Figure 7 (GBPUSD H4, 25 points), du 10 juillet au 18 octobre :
Fig. 7. 7-10 "épaules" croissantes ou "tailles" décroissantes successivement
Comme on peut le voir sur l'image, sept épaules ascendantes sont suivies d'une baisse assez importante à peu près égale à la moitié de la hausse précédente (environ 300 points).
Examinons la stratégie "Trading via une étiquette de prix" pour montrer la nécessité d'utiliser des paramètres d'indicateur supplémentaires. L'idée est d'entrer sur le marché lorsque le prix dépasse (achat) ou passe en dessous (vente) de l'étiquette de prix précédente.
La stratégie est illustrée à la figure 8 (GBPUSD H4, 30 points, construction modifiée) :
Fig. 8. Trading via une étiquette de prix
Les flèches rouges sur la figure 8 indiquent quand acheter ou vendre. Les flèches se déplacent depuis l'étiquette de prix précédente et affichent les emplacements où les étiquettes de prix précédentes ont été franchies.
Les étiquettes temporelles servent principalement de pointeurs de direction de tendance. Étant donné que la couleur des étiquettes temporelles peut être modifiée par rapport au type de ligne et que le type de ligne Yin ou Yang indique la direction ou le retournement de la tendance, la couleur peut nous aider à définir l'humeur actuelle du marché.
Par exemple, prenons le graphique de l'action #IBM (H4, 1%, construction standard) présenté à la Fig. 9 :
Fig. 9. Définition de la direction de la tendance à l'aide d'étiquettes temporelles
Le graphique montre que les lignes bleues sont principalement situées en haut du graphique, tandis que les rouges - en bas.
Conclusion
Le graphique Kagi peut être utilisé avec succès pour le trading sur le marché comme base de stratégie ou comme outil auxiliaire pour une analyse plus précise.
Dans cet article, j'ai examiné le code lui-même et certaines spécificités de construction de l'indicateur. L'objectif principal a été la création de l'indicateur multifonctionnel contenant tous les éléments nécessaires avec quelques fonctionnalités supplémentaires qui peuvent être désactivées.
Je serai heureux d'envisager de nouvelles idées et améliorations pour l'indicateur et, peut-être, de les mettre en œuvre à l'avenir. Aussi, merci de me faire part de vos commentaires. Je me ferai un plaisir de répondre à vos questions concernant l'indicateur.
Cet article poursuit la série consacrée à l'élaboration d'indicateurs pour la construction des graphiques du passé. L'article précédent est disponible ici. La série est à suivre et j'espère vous revoir bientôt. Merci pour ton intérêt! Je vous souhaite un trading réussi, ainsi que des codes optimisés et stables.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/772
- 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