English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Développer un Expert Advisor à partir de zéro (partie 12) : Times et Trade (I)

Développer un Expert Advisor à partir de zéro (partie 12) : Times et Trade (I)

MetaTrader 5Systèmes de trading | 9 janvier 2023, 14:12
603 0
Daniel Jose
Daniel Jose

Introduction

La Lecture de Bande (Tape Reading) est une méthode de trading utilisée par certains traders à différents stades du trading. Cette méthode est très efficace. Lorsqu'elle est utilisée correctement, elle permet une croissance stable des bénéfices d'une manière plus sûre et plus cohérente qu'en utilisant le célèbre Price Action, qui est une pure observation des chandeliers. Mais l'utilisation de la lecture sur bande dans sa forme actuelle est un processus très complexe et fastidieux qui exige une concentration constante et de l'attention. Avec le temps, il est inévitable de faire des erreurs d'observation.

Le problème de la lecture sur bande est lié à la quantité d'informations à analyser. Examinons un cas d'utilisation typique de la lecture de bandes :


Le vrai problème est qu'au cours de l'analyse, nous devons examiner le prix et ce qui lui est arrivé. Mais ce n’est pas pratique de vérifier ces valeurs dans les mini-contrats. Nous ne regardons donc généralement pas le contenu du flux dans les mini-contrats. Nous préférons observer les contrats complets, car ce sont eux qui font bouger le marché. C'est ce qui se passe en réalité. Le système ressemble à celui présenté ci-dessous. C’est un peu plus facile à interpréter et à suivre.


Mais même dans ce cas, l'application du système est un processus très fastidieux qui nécessite une attention extrême. La situation devient encore plus serrée lorsque des positions de stop sont activées. Dans ce cas, nous pouvons manquer une partie du mouvement, car le défilement des informations à l'écran doit être très rapide.


Plan

La plateforme MetaTrader 5 dispose d'un système alternatif, même pour les mini-contrats, ce qui rend le suivi beaucoup plus efficace et plus facile. Voyons comment les choses se présentent lorsqu'on travaille avec des mini-contrats :

Comme vous pouvez le constater, l'interprétation est beaucoup plus facile. Toutefois, pour les raisons que nous avons évoquées plus haut, il est plus simple d'utiliser des contrats complets, qui ressembleront donc à ce qui suit :


Notez que les données sur les transactions sont gênées par le bruit des mouvements des prix BID et ASK. Les transactions sont représentées ici par des cercles. Les cercles rouges indiquent les opérations de vente. Les bleus les opérations d'achat et les verts les ordres directs. Outre le fait que des informations ne nous sont pas nécessaires pour l'observation proprement dite, nous avons un autre problème : le système est séparé du graphique que nous traitons réellement, ce qui nous oblige à surveiller deux écrans. D'un côté, c'est un avantage. Mais dans certains cas, cela complique grandement les choses. Je vous propose donc ici de créer un système qui soit facile à lire et qui nous permette en même temps de voir cet indicateur directement sur le graphique de trading.


Implémentation

La première chose que nous allons faire est de modifier la classe C_Terminal pour pouvoir accéder à l'actif complet du contrat :

void CurrentSymbol(void)
{
        MqlDateTime mdt1;
        string sz0, sz1, sz2;
        datetime dt = TimeLocal();
                
        sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
        m_Infos.szFullSymbol = _Symbol;
        m_Infos.TypeSymbol = ((sz0 == "WDO") || (sz0 == "DOL") ? WDO : ((sz0 == "WIN") || (sz0 == "IND") ? WIN : OTHER));
        if ((sz0 != "WDO") && (sz0 != "DOL") && (sz0 != "WIN") && (sz0 != "IND")) return;
        sz2 = (sz0 == "WDO" ? "DOL" : (sz0 == "WIN" ? "IND" : sz0));
        sz1 = (sz2 == "DOL" ? "FGHJKMNQUVXZ" : "GJMQVZ");
        TimeToStruct(TimeLocal(), mdt1);
        for (int i0 = 0, i1 = mdt1.year - 2000;;)
        {
                m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1);
                m_Infos.szFullSymbol = StringFormat("%s%s%d", sz2, StringSubstr(sz1, i0, 1), i1);
                if (i0 < StringLen(sz1)) i0++; else
                {
                        i0 = 0;
                        i1++;
                }
                if (macroGetDate(dt) < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_EXPIRATION_TIME))) break;
        }
}

// ... Class code ...

inline string GetFullSymbol(void) const { return m_Infos.szFullSymbol; }


En ajoutant les lignes en surbrillance, nous avons accès à l'actif souhaité, que nous utiliserons ensuite dans notre programme Times & Trade. Nous pouvons ensuite passer à la création de la classe d'objets qui supportera notre Times & Trade. Cette classe contient des fonctions très intéressantes. Tout d'abord, il est nécessaire de créer une nouvelle sous-fenêtre qui contiendra notre indicateur. C'est facile à faire. Cependant, pour des raisons pratiques, nous n'utiliserons pas le système de sous-fenêtre que nous avons utilisé précédemment. Peut-être que le concept changera à l'avenir, mais pour l'instant, nous travaillerons avec Times & Trade dans une fenêtre séparée, ce qui implique beaucoup de travail préparatoire.

Commençons par créer un nouveau fichier de support afin d'avoir un nom différent pour l'indicateur. Au lieu de créer des fichiers par-dessus d’autres fichiers, faisons quelque chose de plus élégant. Nous sommes en train de modifier le dossier de support afin d'avoir plus de possibilités. Voici le nouveau fichier de support :

#property copyright "Daniel Jose 07-02-2022 (A)"
#property version   "1.00"
#property description "This file only serves as supporting indicator for SubWin"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
input string user01 = "SubSupport";             //Short Name
//+------------------------------------------------------------------+
int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, user01);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+


J'ai mis en évidence les modifications devant être apportées au fichier source. Maintenant, nous devons apporter des modifications à notre code de l’EA. Nous allons ensuite créer une nouvelle classe :

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Terminal.mqh"
//+------------------------------------------------------------------+
class C_FnSubWin
{
        private :
                string  m_szIndicator;
                int             m_SubWin;
//+------------------------------------------------------------------+
                void Create(const string szIndicator)
                        {
                                int i0;
                                m_szIndicator = szIndicator;
                                if ((i0 = ChartWindowFind(Terminal.Get_ID(), szIndicator)) == -1)
                                        ChartIndicatorAdd(Terminal.Get_ID(), i0 = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL), iCustom(NULL, 0, "::" + def_Resource, szIndicator));
                                m_SubWin = i0;
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_FnSubWin()
                        {
                                m_szIndicator = NULL;
                                m_SubWin = -1;
                        }
//+------------------------------------------------------------------+
                ~C_FnSubWin()
                        {
                                Close();
                        }
//+------------------------------------------------------------------+
                void Close(void)
                        {
                                if (m_SubWin >= 0) ChartIndicatorDelete(Terminal.Get_ID(), m_SubWin, m_szIndicator);
                                m_SubWin = -1;
                        }
//+------------------------------------------------------------------+
inline int GetIdSubWinEA(const string szIndicator = NULL)
                        {
                                if ((szIndicator != NULL) && (m_SubWin < 0)) Create(szIndicator);
                                return m_SubWin;
                        }
//+------------------------------------------------------------------+
inline bool ExistSubWin(void) const { return m_SubWin >= 0; }
//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+


Cette classe remplace C_SubWindow. Elle supporte désormais la création de sous-fenêtres sur un graphique. Pour comprendre le fonctionnement de cette classe, jetez un coup d'œil à la nouvelle classe C_SubWindow ci-dessous :

#include "C_ChartFloating.mqh"
#include <NanoEA-SIMD\Auxiliar\C_FnSubWin.mqh>
//+------------------------------------------------------------------+
class C_SubWindow : public C_ChartFloating
{
//+------------------------------------------------------------------+
        private :
                C_FnSubWin      m_fnSubWin;
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                ~C_SubWindow()
                        {
                                Close();
                        }       
//+------------------------------------------------------------------+
                void Close(void)
                        {
                                m_fnSubWin.Close();
                                CloseAlls();
                        }
//+------------------------------------------------------------------+
inline int GetIdSubWinEA(void)
                        {
                                return m_fnSubWin.GetIdSubWinEA("SubWinSupport");
                        }
//+------------------------------------------------------------------+
inline bool ExistSubWin(void) const { return m_fnSubWin.ExistSubWin(); }
//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+


Faites attention à ce que la classe contienne bien la définition de l'indicateur qui sera utilisé pour soutenir les modèles. Elle est mise en évidence dans le code ci-dessus. La partie délicate vient maintenant. Si nous utilisons un autre nom à la place de SubWinSupport, la classe C_FnSubWin recherchera un autre indicateur. Nous allons utiliser cette astuce pour éviter de créer des fichiers pour les indicateurs. Nous indiquons simplement quel doit être le nom court de l'indicateur souhaité à la classe C_FnSubWin. Nous ne sommes ainsi pas limités par le nombre de sous-fenêtres inutiles ou de fichiers d'indicateurs utilisés uniquement pour créer la sous-fenêtre d’un Expert Advisor.

Nous pouvons ensuite passer à la création de la classe C_TimeAndTrade.


Classe C_TimesAndTrade

La classe d'objets C_TimesAndTrade se compose de plusieurs petits éléments, chacun d'entre eux étant responsable d’un élément ou d’une fonctionnalité spécifique. Le code ci-dessous est la première chose que l'EA appelle dans cette classe :

void Init(const int iScale = 2)
{
        if (!ExistSubWin())
        {
                CreateCustomSymbol();
                CreateChart();
        }
        ObjectSetInteger(Terminal.Get_ID(), m_szObjName, OBJPROP_CHART_SCALE, (iScale > 5 ? 5 : (iScale < 0 ? 0 : iScale)));
}

Ce code va vérifier si la sous-fenêtre de support existe. Si elle n'existe pas encore, le code en créera une. Maintenant, regardez le code suivant de support initial de la classe :

inline void CreateCustomSymbol(void)
{
        m_szCustomSymbol = "_" + Terminal.GetFullSymbol();
        SymbolSelect(Terminal.GetFullSymbol(), true);
        SymbolSelect(m_szCustomSymbol, false);
        CustomSymbolDelete(m_szCustomSymbol);
        CustomSymbolCreate(m_szCustomSymbol, StringFormat("Custom\\Robot\\%s", m_szCustomSymbol), Terminal.GetFullSymbol());
        CustomRatesDelete(m_szCustomSymbol, 0, LONG_MAX);
        CustomTicksDelete(m_szCustomSymbol, 0, LONG_MAX);
        SymbolSelect(m_szCustomSymbol, true);
};

Ce code va créer un symbole personnalisé et réinitialiser toutes les données à l'intérieur de ce symbole. Pour permettre l'affichage du contenu du symbole dans la fenêtre que nous allons créer, il faut d'abord ajouter ce symbole au Market Watch. Nous le faisons avec le code suivant :

SymbolSelect(m_szCustomSymbol, true);

Le symbole personnalisé sera créé dans Custom\Robot <Nom du symbole>. Ses données initiales seront fournies par le symbole original. Il est implémenté dans le code suivant :

CustomSymbolCreate(m_szCustomSymbol, StringFormat("Custom\\Robot\\%s", m_szCustomSymbol), Terminal.GetFullSymbol());

Et c'est tout. Ajoutez la classe à l'EA et exécutez-la de la façon suivante :

// ... Expert Advisor code

#include <NanoEA-SIMD\Tape Reading\C_TimesAndTrade.mqh>

// ... Expert Advisor code

input group "Times & Trade"
input   int     user041 = 2;    //Escala
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_WallPaper     WallPaper;
C_VolumeAtPrice VolumeAtPrice;
C_TimesAndTrade TimesAndTrade;
//+------------------------------------------------------------------+
int OnInit()
{
// ... Expert Advisor code

        TimesAndTrade.Init(user041);
        
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}


Voici le résultat :


C'est exactement ce à quoi on s'attendait. Ajoutons maintenant les valeurs des transactions réalisées sur le graphique _DOLH22. Ce tableau reflétera toutes les transactions effectuées pour fournir la représentation graphique du Times & Trade. La présentation se fera sous la forme de modèles de chandeliers japonais, faciles à utiliser. Avant cela, nous devons effectuer quelques opérations, notamment la connexion et la synchronisation du symbole. Ceci est fait dans le code suivant :

inline void Connect(void)
{
        switch (m_ConnectionStatus)
        {
                case 0:
                        if (!TerminalInfoInteger(TERMINAL_CONNECTED)) return; else m_ConnectionStatus = 1;
                case 1:
                        if (!SymbolIsSynchronized(Terminal.GetFullSymbol())) return; else m_ConnectionStatus = 2;
                case 2:
                        m_LastTime = TimeLocal();
                        m_MemTickTime = macroMinusMinutes(60, m_LastTime) * 1000;
                        m_ConnectionStatus = 3;
                default:
                        break;
        }
}

La fonction vérifie si le terminal est connecté, et synchronise ensuite le symbole. Nous pouvons ensuite commencer à capturer les valeurs et les afficher à l'écran. Mais il est d’abord nécessaire d'apporter une petite modification au code d'initialisation. Ce changement est mis en évidence dans le code suivant :

void Init(const int iScale = 2)
{
        if (!ExistSubWin())
        {
                CreateCustomSymbol();
                CreateChart();
                m_ConnectionStatus = 0;
        }
        ObjectSetInteger(Terminal.Get_ID(), m_szObjName, OBJPROP_CHART_SCALE, (iScale > 5 ? 5 : (iScale < 0 ? 0 : iScale)));
}

Nous pouvons ensuite voir la fonction de capture.

inline void Update(void)
{
        MqlTick Tick[];
        MqlRates Rates[def_SizeBuff];
        int i0, p1, p2 = 0;
        int iflag;

        if (m_ConnectionStatus < 3) return;
        if ((i0 = CopyTicks(Terminal.GetFullSymbol(), Tick, COPY_TICKS_ALL, m_MemTickTime, def_SizeBuff)) > 0)
        {
                for (p1 = 0, p2 = 0; (p1 < i0) && (Tick[p1].time_msc == m_MemTickTime); p1++);
                for (int c0 = p1, c1 = 0; c0 < i0; c0++)
                {
                        if (Tick[c0].volume == 0) continue;
                        iflag = 0;
                        iflag += ((Tick[c0].flags & TICK_FLAG_BUY) == TICK_FLAG_BUY ? 1 : 0);
                        iflag -= ((Tick[c0].flags & TICK_FLAG_SELL) == TICK_FLAG_SELL ? 1 : 0);
                        if (iflag == 0) continue;
                        Rates[c1].high = Tick[c0].ask;
                        Rates[c1].low = Tick[c0].bid;
                        Rates[c1].open = Tick[c0].last;
                        Rates[c1].close = Tick[c0].last + ((Tick[c0].volume > 200 ? 200 : Tick[c0].volume) * (Terminal.GetTypeSymbol() == C_Terminal::WDO ? 0.02 : 1.0) * iflag);
                        Rates[c1].time = m_LastTime;
                        p2++;
                        c1++;
                        m_LastTime += 60;
                }
                CustomRatesUpdate(m_szCustomSymbol, Rates, p2);
                m_MemTickTime = Tick[i0 - 1].time_msc;
        }
}

La fonction ci-dessus permet de capturer absolument tous les ticks de trading pour vérifier s'il s'agit de ticks de vente ou d'achat. Si ces ticks sont liés à des changements des prix BID ou ASK, c'est-à-dire sans volume, l'information n'est pas sauvegardée. Il en va de même pour les ticks qui sont des ordres directs n'affectant pas le mouvement du prix (bien qu'ils soient souvent liés à ce mouvement). Certains acteurs du marché forcent le prix à une certaine valeur uniquement pour exécuter un ordre direct. Ils laissent ensuite le prix évoluer librement. Ces ticks, liés à la modification des prix BID et ASK, seront utilisés dans la version que nous verrons dans le prochain article, car ils sont d'une moindre importance dans le système global. Après avoir vérifié le type de la transaction, il y a une série de lignes très importantes que vous devez comprendre. Ces lignes vont construire un chandelier pour chaque tick passé par le système d'analyse et devant être sauvegardé.

Rates[c1].high = Tick[c0].ask;
Rates[c1].low = Tick[c0].bid;
Rates[c1].open = Tick[c0].last;
Rates[c1].close = Tick[c0].last + ((Tick[c0].volume > 200 ? 200 : Tick[c0].volume) * (Terminal.GetTypeSymbol() == C_Terminal::WDO ? 0.02 : 1.0) * iflag);
Rates[c1].time = m_LastTime;

Le haut et le bas du chandelier indiquent l'écart au moment de la transaction, c'est-à-dire que la valeur qui existait entre le prix BID et le prix ASK et qui sera le corps (ou l'ombre) du chandelier créé. La valeur d'ouverture du chandelier est le prix auquel la transaction a été effectivement réalisée. Regardez maintenant de près la ligne de code mise en évidence. Pour un tick de trading, nous avons un volume. Cette ligne créera un petit ajustement à ce volume afin que l'échelle ne déborde pas. Vous pouvez ajuster les valeurs selon votre propre analyse, en fonction de l'actif et à votre discrétion.

Maintenant le dernier détail : l'heure. Chaque bougie correspondra à une minute car il n'est pas possible de tracer des valeurs inférieures. Chacune d'entre elle restera dans la position correspondante chaque minute. Ce n'est pas du temps réel, mais du temps virtuel. Ne confondez pas le temps du trading avec le temps sur le graphique : les opérations peuvent se dérouler en millisecondes, mais les informations graphiques ne seront tracées que toutes les minutes sur une échelle graphique. Nous pourrions utiliser toute autre valeur, mais celle-ci, étant la plus petite possible, cela simplifie grandement la programmation. Le résultat de ce système peut être vu ci-dessous :

Nous voyons que la lecture est maintenant tout à fait possible et que l'interprétation est simple. Bien que la bande d’ordres ait été très lente au moment de la capture, je pense que c'est suffisant pour se faire une idée.

Les informations finales sur ce système sont données dans la figure ci-dessous :

Attention, 4 configurations différentes sont visibles sur le système. A quoi servent-elles ? Nous verrons cela dans le prochain article, ce qui nous aidera à comprendre pourquoi il existe 4 configurations Times & Trade. Quoi qu'il en soit, nous disposons déjà d'un système fonctionnel qui est peut-être suffisant pour un usage intensif. Mais si vous comprenez ce qui se passe et ce qui fait que les 4 modèles de chandeliers sont générés, vous pouvez tirer beaucoup plus de ce système, et qui sait, peut-être qu'il deviendra votre indicateur principal...


Conclusion

Nous avons créé le système Times & Trade à utiliser dans notre EA pour analyser la lecture des bandes. Il devrait offrir la même vitesse d'analyse que le système alternatif présent dans MetaTrader 5. Nous y sommes parvenus en créant un système graphique, au lieu de lire et d'essayer de comprendre une énorme quantité de chiffres et de valeurs. Dans le prochain article, nous allons implémenter certaines des informations manquantes dans le système. Nous allons devoir ajouter quelques nouveaux éléments au code de notre Expert Advisor.



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

Fichiers joints |
EA_-_Times_e_Trade.zip (5982.95 KB)
Développer un Expert Advisor à partir de zéro (partie 13) : Times &amp; Trade (II) Développer un Expert Advisor à partir de zéro (partie 13) : Times &amp; Trade (II)
Aujourd'hui, nous allons construire la deuxième partie du système Times & Trade pour notre analyse du marché. Dans l'article précédent "Times & Trade (I)", nous avons discuté d'un système graphique alternatif, qui permet d'avoir un indicateur pour une interprétation plus rapide des transactions exécutées sur le marché.
Opérations sur les Matrices et les Vecteurs en MQL5 Opérations sur les Matrices et les Vecteurs en MQL5
Les matrices et les vecteurs ont été introduits en MQL5 pour améliorer les opérations mathématiques. De nouvelles méthodes sont intégrées avec ces nouveaux types pour créer un code concis et compréhensible, proche de la notation mathématique. Les tableaux offrent des possibilités étendues, mais il existe de nombreux cas dans lesquels les matrices sont beaucoup plus efficaces.
Apprenez à concevoir un système de trading basé sur le MFI Apprenez à concevoir un système de trading basé sur le MFI
Le nouvel article de notre série sur la conception d'un système de trading basé sur les indicateurs techniques les plus populaires est basé sur un nouvel indicateur technique : le Money Flow Index (MFI). Nous l'apprendrons en détail et développerons un système de trading simple en MQL5 pour l'exécuter dans MetaTrader 5.
Apprenez à concevoir un système de trading utilisant l’Accumulation/Distribution (AD) Apprenez à concevoir un système de trading utilisant l’Accumulation/Distribution (AD)
Bienvenue dans ce nouvel article de notre série sur l'apprentissage de la conception de systèmes de trading basés sur les indicateurs techniques les plus populaires. Dans cet article, nous découvrirons un nouvel indicateur technique appelé indicateur d'Accumulation/Distribution et découvrirons comment concevoir un système de trading en MQL5 basé sur des stratégies de trading AD Simples.