Développer un Expert Advisor à partir de zéro (partie 30) : CHART TRADE en tant qu'indicateur ?
Introduction
Dans l’article précédent, Développement d'un EA de trading à partir de zéro (partie 29), nous avons supprimé Chart Trade de l'EA. Précédemment, nous avons fait la même chose pour d'autres réflexions, comme Volume At Price et Times & Trade, afin d'améliorer les performances et la fiabilité de l'EA. En supprimant Chart Trade de l'EA, il ne nous reste plus que le système d'ordre de base. Bien que cela puisse sembler insuffisant à certains utilisateurs, l'EA peut en fait faire tout le travail. Il y a des gens qui aiment entrer et sortir des transactions sur le marché, mais ils n'aiment pas les placer en attente. Et ils attendent que le prix atteigne un certain niveau pour entrer ou sortir de la transaction.
Lorsque nous utilisons la plateforme MetaTrader 5 avec l'actif que nous négocions (je mentionne ceci parce que nous pouvons utiliser le système d'ordres croisés, dont nous avons parlé dans la partie 11 de cette série), nous aurons accès au TRADING RAPIDE avec des boutons qui placent des ordres de marché. Ils sont disponibles dans le coin supérieur gauche. Ils se présentent de la manière suivante :
Ces boutons fonctionnent de la même manière que le Chart Trading de base. Mais ils ne peuvent pas être utilisés pour le système d'ordres croisés, car ils ne seront pas du tout visibles. Dans ce cas, nous devrons donc revenir à notre Chart Trade. Mais il ne sera plus utilisé à l'intérieur de l'EA et ne fera pas partie du code de l'EA. Désormais, Chart Trade ne sera plus qu'un indicateur.
Pourquoi devons-nous le faire pour Chart Trading ? La raison en est que l'EA ne doit être responsable que du système de trading. Et que tout ce qui ne fait pas partie de ce système doit être retiré du code de l'EA. Cela peut paraître anodin aujourd'hui, mais ce sera bientôt plus clair puisque je prépare déjà la suite de cet article où j'expliquerai la raison exacte de ce phénomène.
Il serait même possible d'utiliser Chart Trading comme un script, mais cela présenterait un inconvénient : chaque fois que l'on modifie la période du graphique, le script se fermerait et il faudrait l'exécuter à nouveau manuellement.
Ce n'est pas le cas si on l'utilise comme indicateur. L'une des raisons est que Chart Trade n'affectera pas le thread d'exécution des indicateurs, et l'EA sera donc libre, se concentrant sur son code et uniquement sur ce qui concerne la gestion des ordres et des positions.
Bien que nous ne disposions pas de tous les contrôles et de toutes les informations de la version originale, ce Chart Trade en tant qu'indicateur est beaucoup plus simple et fonctionne toujours. Le système MetaTrader 5 est très fonctionnel et pourtant simple. Cependant, il ne permet pas d'accéder à certaines informations dont nous disposerons dans notre Chart Trade.
Commençons donc — le sujet va être très intéressant.
2.0. Développer un indicateur Chart Trade
Pour construire cet indicateur, nous devons effectuer un certain nombre de changements. Nous allons mettre en œuvre ces changements afin de renvoyer Chart Trade sur le graphique sans l'intégrer au code de l'EA.
À première vue, il peut sembler que le code présenté ci-dessous suffise pour que Chart Trade soit au moins compilé. Mais non. Car il est intrinsèquement lié à plusieurs choses que nous devons désactiver. Nous ne le supprimons pas complètement, car nous pourrions vouloir le réintégrer un jour dans l'EA.
#property copyright "Daniel Jose" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ #include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh> //+------------------------------------------------------------------+ C_TemplateChart Chart; C_Terminal Terminal; //+------------------------------------------------------------------+ int OnInit() { Chart.AddThese("IDE(,,170, 240)"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
Si vous essayez de compiler ce code, vous obtiendrez un grand nombre d'erreurs. Mais nous les corrigerons une par une pour que le code reste compatible avec l'EA, tout en l'adaptant à une utilisation en tant qu'indicateur.
La première chose à faire est d'isoler le système qui crée ou gère les sous-fenêtres. Comme Chart Trade n'utilise pas ces sous-fenêtres, il n'est pas nécessaire d'intégrer ce code dans l'indicateur. C'est assez facile à faire. Nous devons modifier le fichier C_Chart_IDE.mqh pour qu'il se présente comme suit :
#ifdef def_INTEGRATION_CHART_TRADER #include <NanoEA-SIMD\SubWindow\C_SubWindow.mqh> #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh> #else #include <NanoEA-SIMD\SubWindow\C_ChartFloating.mqh> #include <NanoEA-SIMD\Auxiliar\C_Terminal.mqh> #endif //+------------------------------------------------------------------+ #ifdef def_INTEGRATION_CHART_TRADER class C_Chart_IDE : public C_SubWindow #else class C_Chart_IDE : public C_ChartFloating #endif
Nous isolons ainsi complètement le système des sous-fenêtres, tout en évitant toute connexion entre Chart Trade et le système d'affichage des ordres de l'EA. Notez que ceci sera contrôlé par la variable d'environnement def_INTEGRATION_CHART_TRADER. Mais comme cette définition n'est utilisée que dans l'EA, tout ce qu'elle contient ne sera pas compilé dans l'indicateur.
Puisque l'indicateur n'utilisera pas de sous-fenêtres, nous devons également contourner certaines choses. L'une d'entre elles est présentée ci-dessous :
bool Create(bool bFloat) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA()); m_szLine = ""; while (m_szLine != "</chart>") // ... Rest of the code...
GetIdSubWinEA renvoie le numéro de la fenêtre dans laquelle se trouve l'indicateur. Cet appel est effectué à plusieurs endroits. Il existe 2 solutions à ce problème. La première solution consiste à modifier la fonction ci-dessus comme suit à chaque endroit où l'appel est fait.
bool Create(bool bFloat) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; #ifdef def_INTEGRATION_CHART_TRADER m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA()); #else m_SubWindow = 0; #endif // ... The rest of the code...
Cela résoudrait le problème, mais nous devrions alors apporter tellement de changements qu'au bout d'un certain temps, le code deviendrait trop difficile à comprendre, car il y aurait trop de directives de compilation conditionnelle. J'ai une solution beaucoup plus simple, mais tout aussi efficace : pour "émuler" cet appel et tout autre appel à travers la définition, nous pouvons simplement ajouter l'extrait suivant dans le code du système.
#ifndef def_INTEGRATION_CHART_TRADER #define GetIdSubWinEA() 0 #define ExistSubWin() false #endif
Ce code résoudra le problème de l'appel. Cela suffira (avec quelques détails supplémentaires) à rendre l'indicateur compilable.
Le deuxième problème majeur est lié aux fonctions impliquées dans les événements de la souris, non pas la souris elle-même mais la classe C_Mouse.
Il faut comprendre que lorsque Chart Trade faisait partie de l'EA, il était possible d'accéder aux positions de la souris (entre autres choses). Mais si nous ajoutons simplement la classe C_Mouse à l'indicateur, nous aurons un conflit entre l'indicateur et l'EA. Je ne vais pas montrer maintenant comment le conflit est résolu. Nous allons prendre une autre direction, mais seulement temporairement, jusqu'à ce que tout soit résolu et que nous retrouvions au moins une partie des fonctionnalités de la version originale de Chart Trade.
Pour cela, nous devons déplacer certains éléments de la classe C_Mouse vers notre nouvel indicateur. Mais ne vous inquiétez pas, ce n'est pas grand-chose. La première modification intervient dans la classe C_TemplateChart — nous apporterons la modification suivante dans sa fonction DispatchMessage :
void DispatchMessage(int id, long lparam, double dparam, string sparam) { datetime dt; double p; C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam); switch (id) { case CHARTEVENT_MOUSE_MOVE: #ifdef def_INTEGRATION_CHART_TRADER Mouse.GetPositionDP(dt, p); #else { int w; ChartXYToTimePrice(Terminal.Get_ID(), (int)lparam, (int)dparam, w, dt, p); } #endif for (int c0 = 0; c0 < m_Counter; c0++) if (m_Info[c0].szVLine != "") // ... Rest of the code...
En ajoutant la section mise en évidence, nous obtenons déjà la fonctionnalité comme si la classe C_Mouse existait toujours. La prochaine modification sera apportée à la classe C_ChartFloating, où nous ferons quelque chose de similaire à ce que nous avons fait précédemment. Mais le code est un peu différent :
void DispatchMessage(int id, long lparam, double dparam, string sparam) { int mx, my; datetime dt; double p; static int six = -1, siy = -1, sic = -1; switch (id) { case CHARTEVENT_MOUSE_MOVE: #ifdef def_INTEGRATION_CHART_TRADER Mouse.GetPositionXY(mx, my); if ((Mouse.GetButtonStatus() & 0x01) == 1) #else mx = (int)lparam; my = (int)dparam; if (((uint)sparam & 0x01) == 1) #endif { if (sic == -1) for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--) // ... Rest of the code...
Nous pouvons maintenant compiler l'indicateur et obtenir le résultat montré dans la vidéo ci-dessous :
Bien que l'EA ne soit pas encore totalement fonctionnel, nous devons prendre la décision suivante : soit Chart Trade aura les mêmes capacités que l'EA, soit elles peuvent être réduites pour obtenir quelque chose entre ce qu'il était auparavant et ce qui est actuellement fourni dans MetaTrader 5. Comme ma position est assez radicale, je vais laisser Chart Trade avec presque les mêmes caractéristiques que celles qu'il avait dans l'EA. Vous pouvez les réduire si vous le souhaitez.
Une fois cette décision prise, nous pouvons passer au sujet suivant puisque Chart Trade vient de devenir un indicateur.
2.1. Comment rendre l'indicateur Chart Trade fonctionnel ?
Les choses vont maintenant se compliquer. Voyons donc comment rendre cet indicateur fonctionnel afin de pouvoir envoyer des ordres, clôturer des positions et même rapporter les résultats des transactions. En fait, ce n'est pas aussi difficile qu'il n'y paraît à première vue, car la plateforme MetaTrader 5 offre un chemin à suivre, de sorte que cela peut être fait avec un minimum d'effort.
Nous ferons ici la même chose que dans la partie 16, dans laquelle nous avons utilisé certaines des fonctionnalités de MetaTrader 5 pour créer un système client-serveur interne (à l'intérieur de la plateforme) afin de transférer des données entre différents processus. Nous ferons ici quelque chose de similaire, mais la simulation sera légèrement différente. Nous avons besoin d'une communication bidirectionnelle, qui doit rester invisible pour l'utilisateur.
La méthode que nous allons utiliser n'est pas la seule possible. Il existe d'autres moyens, comme l'utilisation d'une DLL par exemple, pour permettre ce transfert. Mais nous utiliserons les variables de MetaTrader 5 car elles sont de loin les plus faciles à utiliser, à maintenir et à modifier selon les besoins.
Maintenant que nous avons décidé d'emprunter cette voie, il nous reste à prendre une deuxième décision importante : qui sera le serveur et qui sera le client ? Cette décision aura une incidence sur la manière dont le système sera effectivement mis en œuvre. Quoi qu'il en soit, nous aurons déjà notre protocole de messagerie, dont la mise en œuvre est illustrée ci-dessous :
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_GlobalVariableLeverage (_Symbol + "_Leverage") #define def_GlobalVariableTake (_Symbol + "_Take") #define def_GlobalVariableStop (_Symbol + "_Stop") #define def_GlobalVariableResult (_Symbol + "_Result") #define def_GlobalVariableButton (_Symbol + "_ButtonState") //+------------------------------------------------------------------+ #define def_ButtonDTSelect 0x01 #define def_ButtonSWSelect 0x02 #define def_ButtonBuyMarket 0x04 #define def_ButtonSellMarket 0x08 #define def_ButtonClosePosition 0x10 //+------------------------------------------------------------------+
C'est tout. Il n'est pas nécessaire de déterminer qui sera le client et qui sera le serveur, puisque nous avons déjà défini le protocole de message. Ce protocole doit être défini dès le départ car il facilitera le développement de tout le reste.
Veuillez noter que nous utiliserons 5 variables pour chaque actif pour lequel l'ensemble Chart Trade EA est présent. Les noms des variables dépendent de l'actif auquel l'ensemble est lié, ce qui permet d'utiliser l'ensemble pour plusieurs actifs à la fois.
Une question importante se pose alors : qui fera le travail du serveur ? Il sera responsable de la création de ces variables. Personnellement, je trouve plus pratique d'utiliser l'EA comme serveur et de laisser Chart Trade comme client. L'idée est de toujours avoir l'EA sur le graphique alors que Chart Trade sera présent à des moments spécifiques lorsque cela est nécessaire. L'EA sera ainsi responsable de la création de 4 des 5 variables puisque l'une d'entre elles sera chargée d'indiquer quel bouton a été actionné. C'est donc la responsabilité de Chart Trade.
Sur la base de tous ces éléments, le flux de données se présente comme suit :
- L'EA créera des variables globales qui contiendront les valeurs initiales indiquant l'effet de levier, le take profit et le stop loss. Il créera également la variable qui informera sur le résultat du jour, pour que Chart Trade puisse l'afficher à l'utilisateur et qu'il n'y ait pas besoin de chercher cette information ailleurs.
- Chart Trade créera une variable représentant la valeur de l'appui sur le bouton. Cette variable indique à l'EA ce qu'il doit faire, par exemple fermer une position ouverte ou exécuter un achat ou une vente sur le marché. Cette variable n'existera que pendant cette période et cessera d'exister dès que la demande sera complétée par l'EA.
Le flux est simple, mais il garantit que Chart Trade peut interagir avec l'EA, y compris la capacité du système à envoyer des ordres et à fermer des positions, comme c'était le cas auparavant.
Pour s'assurer que Chart Trade n'existera sur le graphique que lorsque l'EA est présent, nous devons ajouter quelques contrôles. Il est inutile d'avoir Chart Trade sur le graphique si l'EA n'est pas disponible pour envoyer des ordres. Ces contrôles comprennent les deux moments suivants :
- Le premier est effectué lors de l'initialisation de l'indicateur
- Le second est effectué lorsqu'un événement se produit sur le graphique
Voyons cela dans le code, afin qu'il soit plus facile pour vous de comprendre le processus. Lors de l'initialisation, le code suivant est exécuté :
#define def_SHORTNAME "CHART TRADE" //+------------------------------------------------------------------+ int OnInit() { long lparam = 0; double dparam = 0.0; string sparam = ""; IndicatorSetString(INDICATOR_SHORTNAME, def_SHORTNAME); if(!GlobalVariableGet(def_GlobalVariableLeverage, dparam)) return INIT_FAILED; Terminal.Init(); Chart.AddThese("IDE(,,170, 215)"); Chart.InitilizeChartTrade(dparam * Terminal.GetVolumeMinimal(), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), true); OnChartEvent(CHARTEVENT_OBJECT_ENDEDIT, lparam, dparam, sparam); return INIT_SUCCEEDED; }
La ligne en surbrillance évaluera l'une des variables globales, dans ce cas celle qui indique le niveau de l'effet de levier. Si cette variable est manquante, l'initialisation échouera. Rappelez-vous que c'est l'EA qui est responsable de la création de cette variable, et non Chart Trade. Ainsi, l'indicateur saura si l'EA est présent ou non sur le graphique. Et lorsque l'EA aura terminé son travail, il supprimera cette variable de MetaTrader 5, ce qui obligera Chart Trade à être supprimé également. Cela se fait au deuxième point, où nous vérifions la même condition — si la variable globale de levier est disponible ou non.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED); Chart.DispatchMessage(id, lparam, dparam, sparam); }
Le point indiqué ci-dessus sera mis en œuvre à une fréquence relativement élevée. Bien qu'il soit tentant de placer un système d'événements OnTime dans un indicateur, cela n'est pas conseillé : tous les indicateurs utilisent le même thread conducteur et le fait de placer un événement OnTime dans un indicateur affectera tous les autres, si cela n'est pas fait avec une extrême prudence.
Si vous pensez que le contrôle ci-dessus perturbe les performances générales de la plateforme, vous pouvez le placer dans un événement OnTime, mais soyez conscient des conséquences.
Quelle que soit la personne qui déclenche la suppression de l'indicateur Chart Trade du graphique, celle-ci se produit au même endroit, comme le montre le schéma ci-dessous :
void OnDeinit(const int reason) { if (reason == REASON_INITFAILED) { Print("Unable to use Chart Trade. The EA is not on the chart of this asset..."); if (!ChartIndicatorDelete(0, 0, def_SHORTNAME)) Print("Unable to delete Chart Trade from the chart."); } }
La ligne sélectionnée supprime l'indicateur du graphique. En cas d'échec, un message correspondant s'affiche. Ces messages sont visibles dans la boîte à outils, comme indiqué ci-dessous :
Vous devez donc toujours être conscient des informations présentes dans cette fenêtre.
Il manque une dernière fonction avant de passer à une analyse plus approfondie des changements apportés à la classe C_Chart_IDE. Cette fonction est la suivante :
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { double value; if (GlobalVariableGet(def_GlobalVariableResult, value)) { GlobalVariableDel(def_GlobalVariableResult); Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, value, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]); } return rates_total; }
Cette fonction permet de surveiller les variables globales. Ainsi, de temps en temps, lors de la fermeture d'une position, l'EA créera une variable globale dont le nom est défini dans la rubrique def_GlobalVariableResult. Une fois que cette variable est créée et que son nom correspond à celui observé par Chart Trade, la valeur de cette variable sera capturée et la variable sera immédiatement supprimée. Cela permet d'éviter que l'EA n'envoie la mise à jour avant que le traitement ne soit terminé, ce qui entraînerait la perte de cette mise à jour. Cependant, en capturant la valeur avant la suppression, nous pouvons l'envoyer à la classe Chart Trade responsable du traitement des messages, de sorte que la valeur transmise par l'EA sera affichée dans Chart Trade dès que possible.
Ceci complète la partie initiale dans laquelle nous rendons l'indicateur Chart Trade fonctionnel. La deuxième partie concerne les boutons. Nous devons également les rendre fonctionnels. Cela peut être facilement réalisé dans la fonction de traitement des messages de la fonction C_Chart_IDE :
// ... Previous code ... case CHARTEVENT_OBJECT_CLICK: if (StringSubstr(sparam, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) { Resize(-1); return; } sparam = StringSubstr(sparam, 9, StringLen(sparam)); StringToUpper(sparam); #ifdef def_INTEGRATION_CHART_TRADER if ((sparam == szMsgIDE[eBTN_SELL]) || (sparam == szMsgIDE[eBTN_BUY])) TradeView.ExecuteOrderInMarket(m_BaseFinance.Leverange, m_BaseFinance.FinanceTake, m_BaseFinance.FinanceStop, sparam == szMsgIDE[eBTN_BUY], m_BaseFinance.IsDayTrade); if (sparam == szMsgIDE[eBTN_CANCEL]) { TradeView.CloseAllsPosition(); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false); } #else { union u00 { double Value; ulong c; }u_local; u_local.c = 0; if (sparam == szMsgIDE[eBTN_BUY]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonBuyMarket; else if (sparam == szMsgIDE[eBTN_SELL]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonSellMarket; else if (sparam == szMsgIDE[eBTN_CANCEL]) { u_local.c = def_ButtonClosePosition; ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false); } if (u_local.Value > 0) GlobalVariableSet(def_GlobalVariableButton, u_local.Value); } #endif if (sparam == szMsgIDE[eCHECK_DAYTRADE]) InitilizeChartTrade(0, 0, 0, m_BaseFinance.IsDayTrade ? false : true); break; //... Rest of the code...
Notez que le fragment contient 2 codes. Le code bleu est utilisé lorsque Chart Trade est intégré à l'EA. Le vert est utilisé lorsque Chart Trade est présent en tant qu'indicateur.
Nous sommes intéressés par le code vert. Il créera une variable globale et définira l'état des boutons. Ainsi, si un opérateur ferme une position, la valeur correspondant au bouton de fermeture sera placée dans la variable. Mais si vous passez un ordre de marché, cette valeur sera différente et sera désormais une combinaison de deux autres valeurs : l'une indique si l'ordre est un ordre d'achat ou de vente, et l'autre indique si vous souhaitez effectuer une transaction à la journée ou à plus long terme. C'est tout ce que Chart Trade indiquera à l'EA.
IMPORTANT : Ce système est auto-exclusif, c'est-à-dire que si vous cliquez sur le bouton Vendre et que vous cliquez ensuite sur le bouton Acheter avant que l'EA ne fasse quoi que ce soit, l'EA achètera en fait parce que la valeur indiquant la vente sera perdue en raison de la nouvelle valeur d'achat. De plus, si vous demandez de vendre ou d'acheter alors qu'une position est déjà ouverte, et que vous appuyez sur Annuler avant que l'EA n'effectue la transaction en question, la position sera fermée.
Nous pouvons maintenant passer au code de l'EA pour voir comment il fonctionne dans le nouveau modèle.
2.2. Modifier le code de l'EA pour recevoir des messages de Chart Trade
Cette partie est assez facile, puisqu'il suffit d'ajuster quelques petits détails. Mais n'oubliez pas ce qui suit : après avoir chargé l'EA et Chart Trade, ne modifiez pas les variables globales ou les données contenues dans l'EA. Utilisez le système d’ordres pour cela ou Chart Trade lui-même, sinon vous risquez d'avoir des problèmes. Certains problèmes ont des solutions, d'autres non. Alors, au cas où, utilisez les outils disponibles, ne vous compliquez pas la vie.
Dans l'article précédent (partie 29), tout en promouvant la suppression de Chart Trade, nous avons apporté quelques changements dont une partie sera annulée. Nous n'aurons pas besoin de modifier quoi que ce soit d'autre concernant cette question. Mais, comme nous l'avons déjà mentionné, certaines choses peuvent être corrigées et d'autres non. Dans la prochaine partie de cet article, nous allons donc éliminer quelques petits problèmes dans la relation entre Chart Trade et l'Expert Advisor.
Voyons d'abord ce que nous devrons annuler et activer dans l'EA pour qu'il y ait un certain niveau de communication entre l'EA et Chart Trade.
Tout d'abord, modifions les éléments suivants :
input int user20 = 1; //Leverage input double user21 = 100; //Take Profit input double user22 = 81.74; //Stop Loss input bool EA_user23 = true; //Day Trade ?
La valeur qui indique si l'EA préfère ouvrir des transactions longues ou courtes reste inchangée. Cela devra être fait dans Chart Trade ou dans un ordre en attente que vous placez sur le graphique. Je vous ai déjà montré comment faire dans des articles précédents. Alors passons au code. Ajoutons les modifications suivantes dans l'événement OnInit :
int OnInit() { if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Sound.PlayAlert(C_Sounds::TRADE_ALLOWED); return INIT_FAILED; } Terminal.Init(); #ifdef def_INTEGRATION_TAPE_READING VolumeAtPrice.Init(user32, user33, user30, user31); TimesAndTrade.Init(user41); EventSetTimer(1); #endif Mouse.Init(user50, user51, user52); #ifdef def_INTEGRATION_CHART_TRADER static string memSzUser01 = ""; if (memSzUser01 != user01) { Chart.ClearTemplateChart(); Chart.AddThese(memSzUser01 = user01); } Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23); TradeView.Initilize(); OnTrade(); #else GlobalVariableTemp(def_GlobalVariableLeverage); GlobalVariableTemp(def_GlobalVariableTake); GlobalVariableTemp(def_GlobalVariableStop); GlobalVariableTemp(def_GlobalVariableResult); GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal()); GlobalVariableSet(def_GlobalVariableTake, user21); GlobalVariableSet(def_GlobalVariableStop, user22); TradeView.Initilize(); GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof()); #endif return INIT_SUCCEEDED; }
Comme vous pouvez le voir, nous ajoutons ici des variables globales qui seront utilisées pour la communication. Comme nous l'avons déjà mentionné, l'EA doit toujours démarrer avant Chart Trade, sinon nous ne pourrons pas initialiser l'indicateur. Veuillez noter que les valeurs qui seront initialement utilisées par Chart Trade sont spécifiées dans l'EA. L'initialisation est terminée, même si des transactions ont déjà eu lieu : la valeur accumulée est à nouveau transférée dans Chart Trade.
Attention à un détail important : les variables créées sont temporaires, car nous ne voulons pas que ces variables soient sauvegardées en cas de vidage des données de l'EA, étant donné qu'elles ne sont plus utiles après un certain temps. Même s'il arrive quelque chose et que la plateforme s'arrête, ces variables n'existeront plus.
Un autre ajout à mettre en œuvre est présenté ci-dessous :
void OnDeinit(const int reason) { Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
Même si les variables sont temporaires, nous demandons à l'EA de les supprimer de force. Cela permettra d’être sûr que Chart Trade n’est plus sur le graphique. Il y a un petit problème avec cet événement OnDeinit. Mais nous le traiterons dans la prochaine rubrique. Voyons maintenant un autre point assez intéressant. Cette opération peut être réalisée de deux manières différentes. Mais les résultats seront presque identiques. Je dis "presque identiques", car il y a des différences mineures qui peuvent faire la différence. (désolé pour le jeu de mots)
Chart Trade comporte des boutons qui envoient des messages à l'EA par l'intermédiaire d'une variable globale. Voici la fonction à l'intérieur de l'EA qui répond de manière adéquate à ces clics :
inline void ChartTrade_ClickButton(void) { union u00 { double Value; ulong c; }u_local; if (GlobalVariableGet(def_GlobalVariableButton, u_local.Value)) { GlobalVariableDel(def_GlobalVariableButton); if (u_local.c == def_ButtonClosePosition) TradeView.CloseAllsPosition(); else TradeView.ExecuteOrderInMarket(GlobalVariableGet(def_GlobalVariableLeverage), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), ((u_local.c & def_ButtonBuyMarket) == def_ButtonBuyMarket), ((u_local.c & def_ButtonDTSelect) == def_ButtonDTSelect)); TradeView.Initilize(); } }
La fonction est déclarée comme inlinec'est-à-dire qu'elle doit être placée par le compilateur à l'endroit où elle est déclarée. Cela lui permettra de fonctionner le plus rapidement possible.
Un détail très important : où placer cette fonction ? Pensons-y. Nous avons mis en place un système de contrôle afin qu'elle ne fonctionne pas en permanence. Nous avons donc 2 possibilités. La première consiste à placer la fonction dans l'événement OnTick, la seconde dans l'événement OnTime. La sélection doit être basée sur une certaine logique, sinon il peut y avoir des problèmes.
Réfléchissons donc : Si nous plaçons cette fonction dans l'événement OnTick, elle sera exécutée à chaque nouveau tick provenant du serveur de trading vers la plateforme. Cela semble être une bonne solution car la fonction ne sera pas exécutée plusieurs fois, mais seulement à certains moments. Mais cela pose un problème : Si la volatilité de l'actif que nous négocions est très faible, la fréquence des événements OnTick sera également très faible, ce qui signifie que nous pouvons avoir des problèmes avec le déclenchement de l'événement par un clic sur Chart Trade.
Voici la deuxième option. En plaçant la fonction dans OnTime, on s'assure qu'elle sera exécutée puisque l'événement OnTime se déclenchera avec une certaine régularité. Mais nous devons alors nous rappeler que nous ne pourrons plus utiliser l'événement OnTime pour quoi que ce soit d'autre que l'observation des variables globales.
A ce stade, nous avons fait un très bon choix, car l'EA ne fera que surveiller le marché. Au cours des prochains articles, vous verrez que cela deviendra de plus en plus évident. Il est donc judicieux de placer une fonction dans l'événement OnTime. Mais la question qui se pose maintenant est la suivante : l'événement OnTime ne se déclenche que toutes les secondes, n'est-ce pas ?
En fait, le plus souvent, il se déclenche une fois par seconde. Mais nous pouvons définir une période plus courte à l'aide de la fonction EventSetMillisecondTimer. Nous pouvons ainsi déclencher l'événement en moins d'une seconde. Je pense que 500 ms suffiraient dans la plupart des cas. C'est pourquoi nous ajouterons la ligne suivante dans l'événement OnInit de l'EA :
#else GlobalVariableTemp(def_GlobalVariableLeverage); GlobalVariableTemp(def_GlobalVariableTake); GlobalVariableTemp(def_GlobalVariableStop); GlobalVariableTemp(def_GlobalVariableResult); GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal()); GlobalVariableSet(def_GlobalVariableTake, user21); GlobalVariableSet(def_GlobalVariableStop, user22); TradeView.Initilize(); GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof()); EventSetMillisecondTimer(500); #endif
N'oubliez pas de fermer cet événement dans la fonction OnDeinit, pour laquelle nous ajoutons simplement la ligne suivante dans l'événement :
void OnDeinit(const int reason) { EventKillTimer();
Voyons maintenant à quoi ressemble l'événement OnTime. Il s'agit d'un code très simple, où seule la ligne surlignée est compilée.
void OnTimer() { #ifndef def_INTEGRATION_CHART_TRADER ChartTrade_ClickButton(); #endif #ifdef def_INTEGRATION_TAPE_READING VolumeAtPrice.Update(); TimesAndTrade.Connect(); #endif }
Est-ce que c'est tout ce qu’il y a à faire ? Non, il y a un autre petit problème. Rappelons que nous avons modifié les variables d'initialisation de l'EA et que nous souhaitons récupérer les données de Chart Trade chaque fois qu'un nouvel ordre en attente est placé sur le graphique. Pour ce faire, nous devons ajouter un petit détail dans le code de la classe C_IndicatorTradeView. Le voici :
case CHARTEVENT_MOUSE_MOVE: Mouse.GetPositionDP(dt, price); mKeys = Mouse.GetButtonStatus(); bEClick = (mKeys & 0x01) == 0x01; //Left mouse button click bKeyBuy = (mKeys & 0x04) == 0x04; //SHIFT pressed bKeySell = (mKeys & 0x08) == 0x08; //CTRL pressed if (bKeyBuy != bKeySell) { if (!bMounting) { #ifdef def_INTEGRATION_CHART_TRADER m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl); #else m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal(); valueTp = GlobalVariableGet(def_GlobalVariableTake); valueSl = GlobalVariableGet(def_GlobalVariableStop); m_Selection.bIsDayTrade = EA_user23; #endif
Le code en surbrillance capture maintenant les valeurs des variables globales, de sorte que tout ce qui se trouve dans Chart Trade sera placé dans le système d’ordres. Le seul détail est que tous les ordres en attente suivront l'heure spécifiée par l'EA, mais celle-ci peut être modifiée directement sur le graphique. Pour plus de détails, veuillez consulter Développer un Expert Advisor à partir de zéro (partie 27), dans laquelle j'ai montré comment modifier les ordres en attente directement sur le graphique, sans avoir à passer par Chart Trade.
Le système de sonorisation a également été légèrement modifié. Les sons ont été ajoutés dans la classe C_Router pour assurer la notification de l'ouverture et de la fermeture de la position si nous travaillons via Chart Trade. Si vous souhaitez supprimer ou modifier un élément du système de sonorisation, n'oubliez pas qu'il y a d'autres points à prendre en considération.
Avant de terminer, rappelons que l'événement OnDeinit présente un problème que nous allions résoudre. Passons donc au sujet suivant et réglons ce problème.
2.3. Empêcher Chart Trade de sortir prématurément du graphique
Lorsque nous faisons quelque chose, comme changer la période du graphique ou les paramètres de l'EA (cela peut être n'importe quoi), MetaTrader 5 génère un événement OnDeinit. Le déclenchement de cet événement signifie que les choses doivent être analysées de nouveau pour nous assurer que tout continue à fonctionner comme il se doit.
Dans la plupart des cas, le déclenchement de cet événement ne pose aucun problème. Mais comme nous créons un système client-serveur et que nous utilisons des variables globales pour savoir si le serveur (l'EA) a cessé de fonctionner, nous devrons comprendre comment contourner certaines situations.
La fonction qui gère l'événement original OnDeinit ressemble à ceci :
void OnDeinit(const int reason) { EventKillTimer(); Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
La ligne surlignée supprimera la variable globale, exactement celle que nous utilisons dans l'indicateur Chart Trade pour vérifier si l'EA est sur le graphique ou non. Ce contrôle est effectué dans le code suivant :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED); Chart.DispatchMessage(id, lparam, dparam, sparam); }
En d'autres termes, chaque fois qu'un événement activant l'événement OnDeinit se produit dans l'EA, cette variable est supprimée. Et avant que l'EA ne recrée la variable, l'indicateur peut déjà être supprimé du graphique, ce qui peut être très étrange. À certains moments, cela se produira et à d'autres, non. Nous devons donc nous assurer d'une manière ou d'une autre que tout fonctionne comme prévu.
J'ai trouvé une solution à ce problème dans la documentation. Ceci est illustré dans la section Codes des raisons de dé-initialisation. En examinant ces codes, nous pouvons configurer la fonction de gestion de l'événement OnDeinit de manière à ce que les variables ne soient pas supprimées tant que l'EA n'est pas retiré du graphique.
La solution et le nouveau code de traitement seront donc les suivants :
void OnDeinit(const int reason) { EventKillTimer(); Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER switch (reason) { case REASON_CHARTCHANGE: break; default: GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); }; #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
Désormais, si l'on ne modifie que la période du graphique ou le mode de dessin, nous n’aurons plus les inconvénients liés à la disparition de l'indicateur Chart Trade du graphique. Dans toute autre situation, il peut être retiré du graphique, car nous n'avons pas l'assurance réelle que tout se passera bien. Pour cette raison, une fois que l'EA et Chart Trade ont été chargés, il n'est plus utile de modifier les paramètres de l'EA.
Conclusion
Vous voyez ce qu'un peu de créativité peut faire ? Parfois, nous devons résoudre des problèmes qui semblent insolubles. Il est donc essentiel de toujours vérifier la documentation et de la comprendre pour pouvoir mettre les idées en pratique.
Traduit du portugais par MetaQuotes Ltd.
Article original : https://www.mql5.com/pt/articles/10653
- 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