English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
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

MetaTrader 5Exemples | 4 juillet 2022, 11:00
473 0
Daniel Jose
Daniel Jose

Introduction

Dans l'article précédent Indicateurs multiples sur un graphique, nous avons examiné le code de base qui permet d'utiliser plusieurs indicateurs 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. Différentes choses peuvent être développées sur la base de ce modèle. Mais allons-y étape par étape. L'un des objectifs de ces articles est de vous encourager à apprendre à programmer afin que vous puissiez concevoir vos propres systèmes en fonction de vos idées. Dans cet article, nous allons étendre les fonctionnalités. Ceci est intéressant pour ceux qui ont déjà aimé ce que le système peut faire, mais qui aimeraient pouvoir en faire plus.


Planification

Souvent, lorsque nous mettons en œuvre un nouveau système, nous n'avons pas vraiment d'idée sur ce que nous pouvons améliorer. Nous devons donc toujours démarrer un nouveau projet en gardant un œil sur la manière dont il pourrait être soumis à de futures améliorations. C'est un point très important dès le début : imaginer constamment quelque chose en pensant aux extensions possibles et aux améliorations futures.

Le code de base n'a pas du tout changé. Ce qui était déjà prévu. Mais le code de la classe d'objets, lui, a radicalement changé. Nous avons apporté ces modifications pour implémenter de nouvelles fonctionnalités et pour permettre de créer des améliorations futures de manière encore plus flexible, car la réutilisation du code devient encore plus importante (et c'est l'une des idées de base de la Programmation Orientée Objet : toujours réutiliser, créer de nouvelles uniquement si nécessaire). Examinons donc la nouvelle classe d'objets. Je soulignerai les changements pour les rendre plus faciles à comprendre.

Commençons par de nouvelles définitions des variables de classe privée.

struct st
{
        string  szObjName,
                szSymbol;
        int     width;
}m_Info[def_MaxTemplates];
int             m_IdSubWin,
                m_Counter,
                m_CPre,
                m_Aggregate;
long            m_Id,
                m_handle;
ENUM_TIMEFRAMES m_Period;

Attention, le nombre de variables utilisées a considérablement augmenté. En effet, nous avons besoin de plus de données pour gérer correctement la nouvelle fonctionnalité. Maintenant, notre système variable comporte une structure. De telles structures sont très utiles pour regrouper des variables liées ensemble - elles garantissent que lorsque nous manipulons des données, nous y avons accès rapidement et facilement.

void SetBase(const string szSymbol, int iScale, int iSize)
{
#define macro_SetInteger(A, B) ObjectSetInteger(m_Id, m_Info[m_Counter].szObjName, A, B)
        if (m_IdSubWin < 0)
        {
                m_Id = ChartID();
                m_IdSubWin = (int)ChartGetInteger(m_Id, CHART_WINDOWS_TOTAL) - 1;
                m_Aggregate = 0;
        }
        m_Info[m_Counter].szObjName = __FILE__ + (string) MathRand() + (string) ObjectsTotal(m_Id, -1, OBJ_CHART);
        ObjectCreate(m_Id, m_Info[m_Counter].szObjName, OBJ_CHART, m_IdSubWin, 0, 0);
        ObjectSetString(m_Id, m_Info[m_Counter].szObjName, OBJPROP_SYMBOL, (m_Info[m_Counter].szSymbol = szSymbol));

// ....

        macro_SetInteger(OBJPROP_PERIOD, m_Period);
        m_handle = ObjectGetInteger(m_Id, m_Info[m_Counter].szObjName, OBJPROP_CHART_ID);
        m_Aggregate += iSize;
        m_Info[m_Counter].width = iSize;
        m_CPre += (iSize > 0 ? 1 : 0);
        m_Counter++;
#undef macro_SetInteger
};


Le principal changement que nous verrons plus bas est l’utilisation d’une structure pour stocker le nom de l'actif, le nom de l'objet et sa largeur. Maintenant, nous pouvons donc également spécifier la largeur que l'indicateur aura dans la sous-fenêtre. Prenons également quelques notes pour les utiliser dans d'autres parties de la classe. Ci-dessous, la fonction qui a été la plus modifiée.

void Decode(string &szArg, int &iScale, int &iSize)
{
#define def_ScaleDefault 4
#define macro_GetData(A)                \
        b0 = false;                     \
        for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++);     \
        for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != A); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++);        \
        if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = "";
                                                                
        string sz1;
        int i0, i1, c1 = StringLen(szArg);
        bool b0 = true;
        StringToUpper(szArg);
        iScale = def_ScaleDefault;
        m_Period = _Period;
        for (int c0 = 0, max = StringLen(szArg); c0 < max; c0++) switch (szArg[c0])
        {
                case ':':
                        b0 = false;
                        for (; (c0 < max) && ((szArg[c0] < '0') || (szArg[c0] > '9')); c0++);
                        iScale = (int)(szArg[c0] - '0');
                        iScale = ((iScale > 5) || (iScale < 0) ? def_ScaleDefault : iScale);
                        break;
                case ' ':
                        break;
                case '<':
                        macro_GetData('>');
                        if (sz1 == "1M") m_Period = PERIOD_M1; else

//....

                        if (sz1 == "1MES") m_Period = PERIOD_MN1;
                        break;
                case '[':
                        macro_GetData(']');
                        iSize = (int) StringToInteger(sz1);
                        break;
                default:
                        c1 = (b0 ? c0 : c1);
                        break;
        }
        szArg = StringSubstr(szArg, 0, c1 + 1);
#undef macro_GetData
#undef def_ScaleDefault
}


Le vert indique les additions de code. Le jaune est utilisé pour les lignes qui existaient déjà dans le code source, mais qui ont été déplacées pour des raisons pratiques. Voyons maintenant l’ensemble de ces ajouts et, plus important encore, ce qu'ils améliorent en termes de fonctionnalité du système d'origine. Nous créons ici une base pour permettre à l'utilisateur de personnaliser certains éléments spécifiques. Nous essayons de le faire en ajoutant de nouvelles règles à la syntaxe existante (voir le tableau ci-dessous) :

Séparateur Fonctionnalité Exemple  Résultat 
< > Spécifie la période graphique à utiliser  < 15m > Fixe la période de l'indicateur à 15 minutes. Le graphique d'origine peut utiliser une période différente, mais l'indicateur ne sera affiché qu'avec des données de la période 15 minutes.
 [ ] Spécifie la largeur de l'indicateur  [ 350 ] Fixe la largeur de l'indicateur à 350 pixels. 

Le séparateur qui fixe la période du graphique, uniquement dans la fenêtre de l'indicateur, n'affecte pas les modifications que l'utilisateur peut apporter. Tous les autres indicateurs et le graphique principal seront mis à jour avec la nouvelle période sélectionnée par l'utilisateur, mais l'indicateur fixe ne suivra pas la nouvelle période de graphique. Dans certains cas, cela peut être intéressant, comme le montre l'image ci-dessous.

         

Cela facilite grandement l’utilisation de différents types de paramètres, où nous devons conserver des graphiques du même symbole avec différentes périodes visibles sur l'écran de trading. Maintenant, un séparateur qui fixe la largeur du graphique le facilitera dans des cas encore plus spécifiques. Il a également une autre utilisation que nous explorerons dans un autre article. Pour l'instant, vous pouvez ne l'utiliser que pour contrôler la largeur de l'indicateur.

Vous pouvez utiliser une combinaison de tous les séparateurs, ou seulement celui dont vous avez vraiment besoin. Il n'y a pas de règles spécifiques. La seule règle est que le nom de l'indicateur doit être placé avant tout le reste. Revenons à l'explication du code. Regardez les lignes suivantes :

#define macro_GetData(A)                \
        b0 = false;                     \
        for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++);     \
        for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != A); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++);        \
        if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = "";
           
//....                                                     
                case '<':
                        macro_GetData('>');


Notez que nous définissons ici quelque chose qui peut sembler étrange à beaucoup. Mais son nom peut être utile ici : macro_GetData(A), cela crée un morceau de code qui est une macro. Lorsque le compilateur trouve cette définition dans le code, il remplace la déclaration par le code de la macro. Cela est très utile si nous devons répéter une certaine partie de code à plusieurs endroits, mais avec des changements minimes entre une déclaration et une autre. Dans l'exemple précédent, la ligne en vert sera remplacée et le code généré par le compilateur sera le suivant :

case '<':
	b0 = false;
        for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++);
        for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != '>'); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++);
        if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = "";
//....

C’est un bon exemple de l’expression : réutiliser autant que possible, écrire le moins de nouveautés possible. Voyons maintenant ce qui peut être changé si nous voulons rendre la syntaxe encore plus claire. C'est un petit détail, mais il peut être beaucoup plus agréable s'il est adapté à votre style personnel. Jetez un œil à la ligne suivante :

//.....

if (sz1 == "1M") m_Period = PERIOD_M1; else

//.....

L'information mise en évidence est un détail important. Selon le modèle que j'ai utilisé, si l'utilisateur veut utiliser une période de 1 minute, il doit l'indiquer en utilisant la syntaxe suivante : MIN_1. Si vous voulez avoir un autre style, vous pouvez le spécifier à votre façon. Mais les lettres doivent être en majuscules et sans espaces. Par exemple, la partie sélectionnée peut être remplacée par "1MIN", "MIN_1", "1_MINUTE" ou quelque chose avec encore plus de détails, par exemple : "LOCK_IN_1_MIN" - cela fonctionnera tant qu'il n'y aura pas d'espaces entre les mots. En fait, cette restriction d'espace peut être supprimée, mais il me semble qu'il n'y a pas vraiment besoin de le faire. Voyez à quel point il est agréable de connaître la programmation - vous pouvez utiliser les choses avec votre propre style. Le code suivant que j'ai modifié est le destructeur par défaut.

~C_TemplateChart() 
{
        for (char c0 = 0; c0 < m_Counter; c0++)
        {
                ObjectDelete(m_Id, m_Info[c0].szObjName);
                SymbolSelect(m_Info[c0].szSymbol, false);
        }
}

La ligne en surbrillance a été ajoutée pour les cas suivants : si un actif n'est pas ouvert dans une fenêtre séparée, il ne doit plus apparaître dans le Market Watch. Cela évite que des symboles inutilisés ne soient là, prennent de la place et polluent la fenêtre. Voyons maintenant ce que j'ai promis de vous expliquer dans l'article précédent. Ce qui suite n'existait pas dans le code d'origine, mais cela fera partie du code à l'avenir.

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

La ligne en surbrillance permet d'utiliser un fichier de paramètres par défaut si tous les symboles utilisent les mêmes paramètres. Il ne sert à rien de faire quelque chose si vous ne comprenez pas comment agir en cas de problème. Mais si les paramètres sont EXACTEMENT les mêmes, pourquoi ne pas les utiliser ? Veuillez noter que si un fichier de paramètres pour le symbole spécifié n'est pas trouvé, les paramètres par défaut de MetaTrader 5 définis dans le fichier DEFAULT.TPL (qui se trouve dans le répertoire Profiles\Template) seront utilisés. Mais nous devons d'abord comprendre un point important. Pourquoi n'ai-je pas indiqué un répertoire dans la fonction ChartApplyTemplate ? En effet, MetaTrader 5 effectue la recherche en suivant une certaine logique. Savoir comment cette logique fonctionne permet de comprendre les situations de manière plus intéressante et moins stressée.

Imaginez le scénario suivant où nous remplaçons la ligne en surbrillance par celle-ci :

if (!ChartApplyTemplate(m_handle, "MyTemplates\\" + szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, ""MyTemplates\\Default.tpl");

MetaTrader 5 recherchera d'abord le fichier de paramètres dans le sous-répertoire MYTEMPLATES du répertoire dans lequel se trouve le fichier exécutable de l'indicateur personnalisé. C'est-à-dire que si le dossier MYTEMPLATES existe dans le même dossier où se trouve le fichier exécutable de l’indicateur, MetaTrader 5 recherchera le fichier exactement là. Cependant, si rien n'y est trouvé, il recherchera le même fichier dans le répertoire MQL5\Profiles\Templates\MyTemplates, c'est pourquoi je ne l'ai pas montré auparavant. Mais ce n’est pas tout. Il y a un autre détail dans le même code :

if (!ChartApplyTemplate(m_handle, "\\MyTemplates\\" + szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, ""\\MyTemplates\\Default.tpl");

Un petit détail qui change tout : maintenant MetaTrader 5 essaiera d'abord de trouver le fichier dans le répertoire MQL5 MyTemplates, et s'il ne trouve pas le fichier, il suivra les étapes décrites ci-dessus. Vous pouvez trouver ces informations dans la documentation ChartApplyTemplate , car je ne veux pas embuer ceux qui ne savent pas comment fonctionne MetaTrader 5. Maintenant que vous comprenez comment la recherche est effectuée, vous pouvez créer des variantes et vous savez où placer le fichier.

La fonction suivante de notre classe qui a subi des modifications importantes est illustrée ci-dessous :

void Resize(void)
{
#define macro_SetInteger(A, B) ObjectSetInteger(m_Id, m_Info[c0].szObjName, A, B)
        int x0 = 0, x1, y = (int)(ChartGetInteger(m_Id, CHART_HEIGHT_IN_PIXELS, m_IdSubWin));
        x1 = (int)((ChartGetInteger(m_Id, CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1));
        for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++)
        {
                macro_SetInteger(OBJPROP_XDISTANCE, x0);
                macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1));
                macro_SetInteger(OBJPROP_YSIZE, y);
        }
        ChartRedraw();
#undef macro_SetInteger
}

Les lignes sélectionnées montrent les calculs les plus importants de cette fonction. Elles ajustent les fenêtres comme l'utilisateur le souhaite. Mais les fenêtres qui ont été définies avec une taille fixe créeront une zone libre où les autres fenêtres auront leurs tailles définies. Mais si la largeur de la fenêtre principale est réduite, le calcul en bleu ne permettra pas de réduire la largeur des fenêtres de taille fixe, ce qui peut poser problème. Mais laissons ce code tel quel pour le moment, car les fenêtres à largeur fixe présentent d'autres avantages qui seront explorés à l'avenir. Le dernier changement dans notre classe est le suivant :

void AddThese(const eTypeChart type, string szArg)
{
        string szLoc;
        int i0, iSize;
//....
        Decode(szLoc, i0, iSize);
        AddTemplate(type, szLoc, i0, iSize);
//....
}

Le seul changement est mis en surbrillance, il n'y a rien de spécial.


Conclusion

J'espère que cet article, comme les autres, montre à quel point la programmation structurée est intéressante. Avec quelques ajustements et une certaine tranquillité d'esprit, nous pouvons ajouter par la suite beaucoup de fonctionnalités au système. La réutilisation du code rendra la maintenance beaucoup plus facile, car plus nous utilisons le code propre, moins il est susceptible de contenir des bugs. Dans le prochain article, nous amènerons ce système à d'autres endroits où il peut être plus intéressant pour beaucoup et nous essaierons de comprendre plus de détails sur la programmation. À bientôt...



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

Apprenez à concevoir un système de trading utilisant le Momentum Apprenez à concevoir un système de trading utilisant le Momentum
Dans mon article précédent, j'ai mentionné l'importance d'identifier la tendance, donc la direction des prix. Dans cet article, je vais vous présenter l'un des concepts et des indicateurs les plus importants, à savoir l'indicateur Momentum. Je vais vous expliquer comment concevoir un système de trading basé sur l’indicateur Momentum.
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 04) : Passer à un Expert Advisor Plusieurs indicateurs sur un seul graphique (Partie 04) : Passer à un Expert Advisor
Dans mes articles précédents, j'ai expliqué comment créer un indicateur utilisant plusieurs sous-fenêtres, ce qui est intéressant lorsqu'on utilise des indicateurs personnalisés. Cette fois-ci, nous allons voir comment ajouter plusieurs fenêtres à un même Expert Advisor.
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
Dans l'article précédent "Plusieurs indicateurs sur un même graphique", j'ai présenté le concept et les bases de l'utilisation de plusieurs indicateurs sur un même graphique. Dans cet article, je vais vous fournir le code source et l'expliquer plus en détail.