English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Plusieurs indicateurs sur un même graphique (partie 02) : Premières expériences

Plusieurs indicateurs sur un même graphique (partie 02) : Premières expériences

MetaTrader 5Exemples | 4 juillet 2022, 10:40
361 0
Daniel Jose
Daniel Jose

Introduction

Dans l'article précédent "Plusieurs indicateurs sur un seul graphique", j'ai présenté le concept et les bases de l'utilisation de plusieurs indicateurs sur un seul graphique, sans remplir l'écran avec trop de détails. Le seul but de cet article était de présenter le système lui-même, de montrer comment créer des bases de données et comment en tirer parti. Et je n'avais pas fourni le code du système. Ici, nous commencerons à écrire le code. Dans les prochains articles, nous étendrons les fonctionnalités du système, le rendant plus polyvalent et plus complet, car le système semble prometteur et peut être encore amélioré.


Planification

Pour rendre les choses plus faciles, mais surtout pour rendre le système extensible, il a été divisé en deux fichiers distincts, et le code principal utilise les principes de la POO (Programmation Orientée Objet). Tout cela permet de développer le système de façon durable, sûre et stable.

Dans cette première étape, nous allons utiliser l'indicateur, donc créons-en un pour cette fenêtre :

Pourquoi utilisons-nous un indicateur, et pas un autre type de fichier ? La raison est qu'avec un indicateur, il n'est pas nécessaire de mettre en œuvre une logique interne supplémentaire pour créer la sous-fenêtre. Au lieu de cela, nous pouvons demander à l'indicateur de le faire, ce qui nous fait gagner du temps et accélère le développement du système. L'en-tête de l'indicateur se présentera comme suit :

#property indicator_plots 0
#property indicator_separate_window


Avec seulement ces deux lignes, nous pouvons créer une sous-fenêtre sur le graphique d’une symbole (pour ceux qui ne savent pas comment ils fonctionnent, veuillez consulter le tableau ci-dessous) :

Code Description
indicator_plots 0 Cette ligne informe le compilateur que nous ne stockerons aucune données ; elle empêche le compilateur d'afficher des messages d'avertissement.
indicator_separate_window Cette ligne indique au compilateur d'ajouter la logique nécessaire à la création d'une sous-fenêtre.

Cela devrait être facile. Pour ceux qui ne sont pas familiers avec la programmation, certaines lignes dans le code source peuvent sembler étranges, mais elles suivent simplement des modèles largement acceptés et utilisés par l'ensemble des communautés de programmation. Étant donné que MetaTrader 5 utilise le langage MQL5, qui est très similaire au C++, nous pouvons donc utiliser la même méthode de programmation. Les choses deviennent ainsi beaucoup plus faciles. Ainsi, en tirant parti de ce fait, nous pouvons utiliser une directive du langage C comme suit :

 #include <Auxiliary\C_TemplateChart.mqh>

Cette directive indique au compilateur d'inclure un fichier d'en-tête qui existe à un certain endroit. Le chemin complet ne devrait-il pas être Includes \ Auxiliary \C_TemplateChart.mqh!? Effectivement, le chemin complet ressemble à ceci. Mais MQL5 sait déjà que tout fichier d'en-tête doit être situé dans le répertoire 'includes', nous pouvons donc omettre la première partie. Si le chemin est entre crochets, il s'agit d'un chemin absolu ; s'il est entre guillemets, il s'agit d'un chemin relatif, à savoir <Auxiliary \ C_TemplateChart.mqh> est différent de "Auxiliary \ C_TemplateChart.mqh".

En continuant à dérouler le code, nous obtenons les lignes suivantes :

input string user01 = "" ;       //Used indicators
input string user02 = "" ;       //Assets to follow


Des valeurs de type String peuvent être saisies ici. Si vous savez clairement quelle commande vous voulez utiliser pour ouvrir l'indicateur, vous pouvez spécifier ici une valeur par défaut. Par exemple, vous souhaitez toujours utiliser le RSI avec une largeur de ligne de 3 et le MACD avec une largeur de ligne de 2. Spécifiez-le par défaut comme suit :

input string user01 = "RSI:3;MACD:2" ;  //Used indicators
input string user02 = "" ;              //Assets to follow


Les options peuvent bien sûr toujours être modifiées par la suite, mais la valeur par défaut facilite l'ouverture de l'indicateur, car tout sera déjà préconfiguré. La ligne suivante créera un alias, à travers lequel nous pourrons accéder à la classe de l’objet qui contient tout le code, nous permettant d'accéder à ses fonctions publiques.

C_TemplateChart SubWin;

Notre code à l'intérieur du fichier de l'indicateur personnalisé est presque prêt. Nous devons seulement ajouter 3 lignes supplémentaires pour que tout soit prêt et fonctionne. Bien sûr, notre classe d'objets ne contient pas d'erreurs, mais dans cette classe, nous allons voir cela à l'intérieur. Pour terminer le fichier de l’indicateur, ajoutez les lignes suivantes surlignées en vert :

 //+------------------------------------------------------------------+
int OnInit ()
{
         SubWin.AddThese(C_TemplateChart::INDICATOR, user01);
         SubWin.AddThese(C_TemplateChart::SYMBOL, user02);

         return INIT_SUCCEEDED ;
}
//+------------------------------------------------------------------+

//...... other lines are of no interest to us ......

//+------------------------------------------------------------------+
void OnChartEvent ( const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
{
         if (id == CHARTEVENT_CHART_CHANGE ) SubWin.Resize();
}
//+------------------------------------------------------------------+


C'est exactement ce que contiendra l'indicateur personnalisé. Maintenant, regardons de plus près la «boîte noire» du fichier contenant notre classe d'objets. À partir de maintenant, nous devons nous concentrer sur deux fonctions. Pour faciliter les choses, commençons par examiner les fonctions présentes dans notre classe d'objets et voyons à quoi sert chacune d'entre elles.

Fonctions Description
SetBase Crée l’objet nécessaire à l'affichage des données de l'indicateur
decode Décode la commande qui lui est passée
AddTemplate Ajuste les données en fonction du type de données passé en argument
C_Template Constructeur de classe par défaut
~ C_Template Destructeur de classe
Resize Modifie la taille de la sous-fenêtre
AddThese Fonction permettant de construire et d’accéder aux objets internes.

C'est tout. Comme vous le voyez, nous utilisons les fonctions RESIZE et ADDTHESE dans notre indicateur personnalisé. Ce sont les seules fonctions publiques pour l'instant. Ca signifie que nous n'avons pas à nous soucier d’autre chose car tout le reste est caché à l'intérieur de notre objet, ce qui garantit qu'il ne sera pas modifié sans nécessité. Cela assure une grande fiabilité de notre code final. Procédons avec le code qui commence par la définition suivante :

 #define def_MaxTemplates         6

Cette ligne est très importante pour notre classe d'objets car elle définit le nombre maximum de pointeurs pouvant être créés. Pour en ajouter ou en supprimer, il suffit de modifier ce nombre. Avec cette solution simple, nous avons une allocation dynamique de la mémoire et nous limitons le nombre d'indicateurs. C'est probablement le seul point que vous voudrez changer. Mais je pense que 6 est un chiffre convenable pour la plupart des gens et des écrans utilisés.

La ligne suivante est une énumération qui facilite la gestion des données à certains moments du programme :

 enum eTypeChart {INDICATOR, SYMBOL};

Le fait que cette ligne se trouve à l'intérieur de notre classe garantit qu'elle peut avoir le même nom dans une autre classe, mais que les données qui y sont spécifiées ne lui appartiennent qu'à elle. Ainsi, afin d'accéder correctement à cet énumération, il faut utiliser la signature correcte fournie avec la fonction OnInit de notre fichier d'indicateur personnalisé. Si le nom de la classe n’est pas fourni, cela est considéré comme une erreur de syntaxe et le code ne sera pas compilé. La ligne suivante est un mot réservé.

 private :


Cela signifie que tout ce qui vient en-dessous sera ’privé’ pour cette classe d'objets et ne sera donc pas visible en dehors de la classe. L'accès à tout autre élément sera donc impossible si vous n'êtes pas dans la classe. Cela améliore la sécurité du code en rendant les données privées spécifiques à une classe inaccessibles en dehors. D'autres lignes déclarent des variables internes et privées. Et nous arrivons ensuite à la première vraie fonction de notre classe.

 void SetBase( const string szSymbol, int scale)
{
#define macro_SetInteger(A, B) ObjectSetInteger (m_Id, m_szObjName[m_Counter], A, B)

...

         ObjectCreate (m_Id, m_szObjName[m_Counter], OBJ_CHART , m_IdSubWin, 0 , 0 );
         ObjectSetString (m_Id, m_szObjName[m_Counter], OBJPROP_SYMBOL , szSymbol);
        macro_SetInteger( OBJPROP_CHART_SCALE , scale);
...
        macro_SetInteger( OBJPROP_PERIOD , _Period );
        m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID );
        m_Counter++;
#undef macro_SetInteger
};


Examinons plus en détail ce segment de code SetBase. Nous commençons par déclarer une macro - elle indique au compilateur comment le code simplifié portant le nom de la macro doit être interprété. C'est-à-dire que pour éviter de répéter du code plusieurs fois, nous pouvons utiliser cette fonctionnalité du langage C pour produire du code plus simple. Si, par hasard, nous devons changer quelque chose, nous ne le changerons que dans la macro. Cela accélère considérablement le travail et réduit la possibilité d'erreurs dans le code où seul l'un ou l'autre argument sera modifié.

En faisant cela, nous créons un objet de type CHART. Cela peut sembler étrange. Pourquoi utilisons-nous un objet qui peut être changé pour changer d’autres choses ? C'est exact. L'étape suivante consiste à déclarer l'actif, ou le symbole, à utiliser. Premièrement, si au moment de l'enregistrement du graphique aucun actif ou symbole n'est présent, l'objet sera lié à l'actif ou au symbole actuellement utilisé. S'il s'agit d'un tableau d'actifs ou de symboles spécifiques, c'est exactement cet actif ou ce symbole qui sera utilisé par la suite. Détail important : vous pouvez indiquer un actif ou un symbole différent et utiliser des paramètres génériques. Nous y reviendrons en détail dans le prochain article. Parce que nous allons mettre en œuvre certaines améliorations dans le code pour pouvoir faire des choses qui ne sont pas possibles actuellement. Ensuite, il y a le niveau de densification de l'information qui est indiqué dans la propriété OBJPROP_CHART_SCALE. Nous utiliserons des valeurs de 0 à 5. Bien que nous puissions utiliser des valeurs en dehors de cette plage, il vaut mieux se restreindre à ces valeurs.

L'élément suivant auquel il faut prêter attention est la propriété OBJPROP_PERIOD. Veuillez noter que nous utilisons la période en cours du graphique et que si nous la changeons, celle-ci sera également modifiée. À l'avenir, nous apporterons quelques modifications qui permettront de la verrouiller. Si vous voulez l'essayer, vous pouvez utiliser une période définie par MetaTrader 5, par exemple PERIOD_M10, qui indiquera l'affichage des données dans une période fixe de 10 minutes. Mais nous améliorerons cela plus tard. Après cela, nous incrémentons le nombre de sous-indicateurs de 1 et supprimons la macro. C'est-à-dire qu'elle ne sera plus représentée en mémoire et devra être redéfinie pour être utilisée ailleurs. N’ai-je pas oublié quelque chose ? Oui, et c'est la ligne qui est peut-être la partie la plus importante du code.

m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID );

Cette ligne défini un objet qui peut être considéré comme un pointeur (bien que ce ne soit pas vraiment un pointeur lui-même). Il va nous permettre de faire des manipulations supplémentaires sur l'objet OBJ_CHART que nous avons créé. Nous avons besoin de cette valeur pour appliquer certains paramètres à l'intérieur de l'objet. Ils se trouvent dans le fichier de paramètres que nous avons créé précédemment. En continuant à dérouler le code, nous arrivons à la fonction suivante, qui est en entier ci-dessous :

 void AddTemplate( const eTypeChart type, const string szTemplate, int scale)
{
	if (m_Counter >= def_MaxTemplates) return ;
	if (type == SYMBOL) SymbolSelect (szTemplate, true );
	SetBase((type == INDICATOR ? _Symbol : szTemplate), scale);
	ChartApplyTemplate (m_handle, szTemplate + ".tpl" );
	ChartRedraw (m_handle);
}


Nous vérifions d'abord s'il est possible ou non d'ajouter un nouvel indicateur. Si c'est possible, vérifiez s'il s'agit d'un SYMBOLE, et si c'est le cas, le SYMBOLE doit être présent dans la fenêtre du Market Watch, ce qui est garanti par la fonction. Sur cette base, nous créons un nouvel objet qui va recevoir les informations. Lors de l'exécution, le modèle est appliqué à l’objet de type OBJ_CHART, et c'est là que la magie opère : nous appelons à nouveau l'objet, mais il contiendra cette fois les données suivant les définitions contenues dans le fichier de paramètres qui a été utilisé pour définir OBJ_CHART. Maintenant, c'est simple, beau et compréhensible.

On pourrait faire beaucoup de choses avec ces deux fonctions. Mais nous avons également besoin d'une autre fonction. Son code complet est indiqué ci-dessous :

 void Resize( void )
{
         int x0 = 0 , x1 = ( int )( ChartGetInteger (m_Id, CHART_WIDTH_IN_PIXELS , m_IdSubWin) / (m_Counter > 0 ? m_Counter : 1 ));
         for ( char c0 = 0 ; c0 < m_Counter; c0++, x0 += x1)
        {
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XDISTANCE , x0);
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XSIZE , x1);
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_YSIZE , ChartGetInteger (m_Id, CHART_HEIGHT_IN_PIXELS , m_IdSubWin));
        }
         ChartRedraw ();
}

La fonction ci-dessus dispose tout les éléments à leur place, en gardant les données toujours à l'intérieur de la zone de la sous-fenêtre. Il n'y a rien de plus à ajouter ici. Nous avons terminé tout le code nécessaire pour que tout fonctionne parfaitement. Mais qu'en est-il des autres fonctions ? Ne vous inquiétez pas, le reste des fonctions ne sont pas du tout nécessaires, elles ne sont là que pour aider à l’interprétation des lignes de commande. Jetons quand même un coup d'œil à un élément qui est important pour quiconque souhaite modifier ce code à l'avenir. C'est le mot réservé qui apparaît dans notre code de classe d'objet :

 public   :

Ce mot garantit qu'à partir de maintenant, toutes les données et fonctions peuvent être consultées et visualisées par d'autres parties du code, même si elles ne font pas partie de la classe d'objets. Nous déclarons donc ici ce qui peut être modifié ou consulté par d'autres objets. En fait, le comportement d'un bon code orienté objet est de ne jamais permettre un accès direct aux données d'un objet. Dans un code bien conçu, nous n'aurons accès qu'à certaines méthodes. La raison en est simple : la sécurité. Lorsque nous autorisons un code externe à modifier les données d'une classe, nous courons le risque que les données ne correspondent pas à ce que l'objet attend, ce qui causerait beaucoup de problèmes et serait un casse tête lorsqu'on essaie de résoudre des incohérences ou des défauts alors que tout semble correct. Voici donc quelques conseils que je peux donner à quelqu'un qui programme en C++ depuis des années : Ne permettez JAMAIS à des objets externes de modifier ou d'accéder directement aux données d'une classe que vous créez. Fournissez des fonctions permettant d'accéder aux données, mais n'autorisez jamais l'accès aux données directement. Assurez-vous que les fonctions prennent en charge les données comme prévu par la classe que vous avez créée. Maintenant, passons aux deux dernières fonctions de notre tutoriel, dont l'une est publique (AddThese) et l'autre privée (Decode). Vous pouvez les voir dans leur intégralité ci-dessous :

void Decode( string &szArg, int &iScale)
{
#define def_ScaleDefault 4
         StringToUpper (szArg);
        iScale = def_ScaleDefault;
         for ( int c0 = 0 , c1 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0])
        {
                 case ':' :
                         for (; (c0 < max) && ((szArg[c0] < '0' ) || (szArg[c0] > '9' )); c0++);
                        iScale = ( int )(szArg[c0] - '0' );
                        iScale = ((iScale > 5 ) || (iScale < 0 ) ? def_ScaleDefault : iScale);
                        szArg = StringSubstr (szArg, 0 , c1 + 1 );
                         return ;
                 case ' ' :
                         break ;
                 default :
                        c1 = c0;
                         break ;
        }
#undef def_ScaleDefault
}
//+------------------------------------------------------------------+
// ... Codes not related to this part...
//+------------------------------------------------------------------+
void AddThese( const eTypeChart type, string szArg)
{
         string szLoc;
         int i0;
         StringToUpper (szArg);
         StringAdd (szArg, ";" );
         for ( int c0 = 0 , c1 = 0 , c2 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0])
        {
                 case ';' :
                         if (c1 != c2)
                        {
                                szLoc = StringSubstr (szArg, c1, c2 - c1 + 1 );
                                Decode(szLoc, i0);
                                AddTemplate(type, szLoc, i0);
                        }
                        c1 = c2 = (c0 + 1 );
                         break ;
                 case ' ' :
                        c1 = (c1 >= c2 ? c0 + 1 : c1);
                         break ;
                 default :
                        c2 = c0;
                         break ;
        }
}


Ces deux fonctions font exactement ce que j'ai expliqué précédemment : elles assurent l'intégrité des données au sein de la classe de l'objet en empêchant les données incohérentes de faire partie des données internes de la classe. Elles prennent en argument une ligne de commande et la décodent en suivant une syntaxe prédéfinie. Cependant, elles ne retournent pas que la commande reçue comporte une erreur, car ce n'est pas leur but. Leur but est de garantir que des données incohérentes n'entrent pas dans l'objet et ne provoquent pas d'effets secondaires qui peuvent être difficiles à détecter et à corriger.

Le résultat final sera le suivant :



Conclusion

J'espère que ce code vous inspirera ! Je m’intéresse à la programmation parce que c'est beau et excitant. Bien que cela nous donne parfois bien des difficultés si nous voulons obtenir des résultats particuliers. Mais ça en vaut réellement la peine la plupart du temps. Dans le prochain article, je vous dirai comment rendre tout cela encore plus intéressant. En attachement à cet article, vous trouverez le code complet de l'indicateur. Il peut être utilisé comme décrit dans cet article et le précédent.


Traduit du portugais par MetaQuotes Ltd.
Article original : https://www.mql5.com/pt/articles/10230

Apprenez à concevoir un système de trading avec les Enveloppes Apprenez à concevoir un système de trading avec les Enveloppes
Dans cet article, je vais partager avec vous l'une des méthodes de trading avec les bandes. Cette fois, nous nous intéresserons aux enveloppes. Nous verrons à quel point il est facile de créer des stratégies de trading basées sur les enveloppes.
Plusieurs indicateurs sur un seul graphique (Partie 01) : Comprendre les notions Plusieurs indicateurs sur un seul graphique (Partie 01) : Comprendre les notions
Aujourd'hui, nous allons apprendre à ajouter plusieurs indicateurs fonctionnant simultanément sur un graphique, mais sans occuper une zone distincte sur celui-ci. De nombreux traders se sentent plus confiants s'ils surveillent plusieurs indicateurs à la fois (par exemple, RSI, STOCASTIC, MACD, ADX et quelques autres), ou dans certains cas même sur différents actifs dont un indice est composé.
Plusieurs indicateurs sur un seul graphique (Partie 03) : Développer des définitions pour les utilisateurs Plusieurs indicateurs sur un seul graphique (Partie 03) : Développer des définitions pour les utilisateurs
Aujourd'hui, nous allons mettre à jour les fonctionnalités du système d'indicateurs pour la première fois. Dans l'article précédent "Plusieurs indicateurs sur un graphique", nous avons considéré le code de base qui permet d'utiliser plus d'un indicateur dans une sous-fenêtre de graphique. Mais ce qui a été présenté n'était que le point de départ d'un système beaucoup plus vaste.
Apprenez à concevoir un système de trading avec les Bandes de Bollinger Apprenez à concevoir un système de trading avec les Bandes de Bollinger
Dans cet article, nous allons découvrir les Bandes de Bollinger, l'un des indicateurs les plus populaires dans le monde du trading. Nous allons considérer l'analyse technique et voir comment concevoir un système de trading algorithmique basé sur l'indicateur des Bandes de Bollinger.