Développer un Expert Advisor de trading à partir de zéro (Partie 19) : Nouveau système d'ordres (II)
Introduction
Dans l'article précédent, Développer un Expert Advisor de trading à partir de zéro (Partie 18), nous avons mis en place quelques corrections, changements et ajustements dans le système d'ordres, visant à créer un système qui permettrait un trading différent sur les comptes de types NETTING et HEDGING, puisqu'il y a des différences dans les opérations. Pour le type NETTING (compensation), le système génère un prix moyen et vous ne pouvez avoir qu'une seule position ouverte pour un actif. Sur les comptes de type HEDGING (couverture), vous pouvez avoir plusieurs positions ouvertes, chacune ayant des limites individuelles. Vous pouvez acheter et vendre les mêmes actifs en même temps. Cette opération n'est possible que pour les comptes de type HEDGING. C'est sur cette base que l'on peut comprendre le trading d'options.
Il est maintenant temps de rendre le système d’ordre complètement visuel afin d'éliminer la boîte de dialogue et d'analyser les valeurs de chaque position sans cette boîte. Il suffit pour cela d'examiner le nouveau système d’ordres. Cela nous permettra d'ajuster plusieurs choses en même temps. Nous pourrons également facilement connaître les limites de profit et de perte d'une position OCO ou d'un ordre OCO en attente, puisque l'EA affichera ces informations en temps réel, sans nécessiter de calculs supplémentaires.
Bien qu'il s'agisse de la première partie de la mise en œuvre, nous ne partons pas de zéro : nous modifierons le système existant en ajoutant davantage d'objets et d'événements au graphique de l'actif en cours.
1. Plan
Le plan du système que nous utilisons ici n'est pas particulièrement difficile : nous modifierons le système existant en changeant uniquement le système qui représente les ordres sur le graphique. C'est l'idée principale qui semble assez simple. Mais dans la pratique, cela demande beaucoup de créativité : nous allons manipuler et modéliser des données de manière à ce que la plateforme MetaTrader 5 fasse tout le travail à notre place.
Il existe plusieurs façons de modéliser ces données, chacune ayant ses avantages et ses inconvénients.
- La première méthode consiste à utiliser une liste. Il peut s'agir d'une liste simple, d'une double liste ou même d'une table de hachage. L'avantage d'utiliser l'une ou l'autre de ces approches est que le système est facile à mettre en œuvre. L'inconvénient est que cela empêche la manipulation des données ou limite le nombre d'ordres. Dans ce cas, nous devrions également créer toute la logique supplémentaire juste pour enregistrer la liste.
- La deuxième méthode consiste à créer un tableau de classes, qui contiendra et gérera tous les nouveaux objets. Dans ce cas, le tableau fonctionnera comme une liste, mais nous devrons écrire moins de code, car MQL5 prend déjà en charge certaines choses que nous devrions coder dans le cas de l'utilisation d'une liste. Mais nous aurions aussi d'autres problèmes, tels que la gestion des événements, qui, dans cette situation, serait assez difficile.
- La troisième voie est celle que nous allons utiliser. Nous allons forcer le code créé en MQL5 à prendre en charge les objets dynamiques. Cela semble irréel, mais si nous modélisons correctement les données à utiliser, le langage MQL5 nous permettra de créer un système dans lequel il n'y aura aucune restriction sur le nombre d'objets à l'écran. Tous les objets pourront également générer et recevoir des événements. Et malgré leur individualité, la plateforme les verra tous liés comme s'ils étaient dans une liste ou dans un tableau.
Si vous pensez que ce n'est pas facile à mettre en œuvre, jetez un coup d'œil à la partie de code suivante de la classe C_HLineTrade :
inline void SetLineOrder(ulong ticket, double price, eHLineTrade hl, bool select) { string sz0 = def_NameHLineTrade + (string)hl + (string)ticket, sz1; ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0); //... The rest of the code....
La partie surlignée montre que nous pouvons créer autant de lignes horizontales que nous le souhaitons et qu'elles recevront des événements de manière totalement indépendante. Tout ce que nous avons à faire, c'est d'implémenter des événements basés sur le nom que chacune des lignes aura, puisque les noms seront uniques. La plateforme MetaTrader 5 s'occupe du reste. Le résultat ressemblera à ceci :
Bien que cela semble déjà idéal, cette modélisation ne suffira pas à obtenir le résultat dont nous avons réellement besoin. L'idée peut être mise en œuvre. Mais la modélisation des données actuellement disponible dans l'EA n'est pas idéale, car nous ne pouvons pas avoir un nombre illimité d'objets basés sur un seul nom. Nous devons effectuer quelques changements qui nécessitent une modification assez profonde du code.
Nous allons maintenant commencer à mettre en œuvre cette nouvelle méthode de modélisation des données. Nous ne modifierons que ce qui est nécessaire à cette fin, tout en maintenant l'ensemble du code stable, car il doit continuer à fonctionner de la manière la plus régulière possible. Tout le travail sera effectué par la plateforme MetaTrader 5. Nous indiquerons seulement comment la plateforme doit comprendre notre modélisation.
2. Implémentation
La première modification consiste à remplacer C_HLineTrade par la nouvelle classe C_ObjectsTrade. Cette nouvelle classe sera en mesure de répondre à nos besoins, c'est-à-dire de lier un nombre illimité d'objets.
Commençons par examiner les définitions d’origine dans le code suivant :
class C_ObjectsTrade { //+------------------------------------------------------------------+ #define def_NameObjectsTrade "SMD_OT" #define def_SeparatorInfo '*' #define def_IndicatorTicket0 1 //+------------------------------------------------------------------+ protected: enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PRICE}; //+------------------------------------------------------------------+ // ... The rest of the class code
Nous avons ici la base que nous allons mettre en œuvre. Elle sera étendue à l'avenir. Mais pour l'instant, je veux que le système reste stable, même s'il est modifié et que de nouvelles données sont modélisées.
Même au sein de la déclaration "protected", nous avons les fonctions suivantes :
inline double GetLimitsTake(void) const { return m_Limits.TakeProfit; } //+------------------------------------------------------------------+ inline double GetLimitsStop(void) const { return m_Limits.StopLoss; } //+------------------------------------------------------------------+ inline bool GetLimitsIsBuy(void) const { return m_Limits.IsBuy; } //+------------------------------------------------------------------+ inline void SetLimits(double take, double stop, bool isbuy) { m_Limits.IsBuy = isbuy; m_Limits.TakeProfit = (m_Limits.TakeProfit < 0 ? take : (isbuy ? (m_Limits.TakeProfit > take ? m_Limits.TakeProfit : take) : (take > m_Limits.TakeProfit ? m_Limits.TakeProfit : take))); m_Limits.StopLoss = (m_Limits.StopLoss < 0 ? stop : (isbuy ? (m_Limits.StopLoss < stop ? m_Limits.StopLoss : stop) : (stop < m_Limits.StopLoss ? m_Limits.StopLoss : stop))); } //+------------------------------------------------------------------+ inline int GetBaseFinanceLeveRange(void) const { return m_BaseFinance.Leverange; } //+------------------------------------------------------------------+ inline int GetBaseFinanceIsDayTrade(void) const { return m_BaseFinance.IsDayTrade; } //+------------------------------------------------------------------+ inline int GetBaseFinanceTakeProfit(void) const { return m_BaseFinance.FinanceTake; } //+------------------------------------------------------------------+ inline int GetBaseFinanceStopLoss(void) const { return m_BaseFinance.FinanceStop; }
Ces fonctions servent actuellement uniquement de mesure de sécurité pour un autre système que nous mettrons en œuvre à l'avenir. Même si nous pouvons implémenter les données et l'analyse syntaxique à un autre endroit, il est bon de laisser certaines choses aussi bas que possible dans la chaîne d'héritage. Même si les valeurs de retour ne seront utilisées que par les classes dérivées, je ne veux pas l'autoriser directement : Je ne veux pas que la classe dérivée accède aux valeurs contenues dans la classe d'objets C_ObjectsTrade. Cela briserait l'idée d'encapsulation de la classe d'objets, ce qui compliquerait les modifications futures ou les corrections de bugs, si la classe dérivée modifiait la valeur de la classe de base sans effectuer les changements correspondants par le biais d'un appel de procédure.
Pour minimiser autant que possible le chevauchement des appels, toutes les fonctions sont déclarées en ligne : cela augmente légèrement la taille de l'exécutable, mais permet d'obtenir un système plus sûr.
Venons-en maintenant aux déclarations privées :
//+------------------------------------------------------------------+ private : string m_SelectObj; struct st00 { double TakeProfit, StopLoss; bool IsBuy; }m_Limits; struct st01 { int FinanceTake, FinanceStop, Leverange; bool IsDayTrade; }m_BaseFinance; //+------------------------------------------------------------------+ string MountName(ulong ticket, eIndicatorTrade it) { return StringFormat("%s%c%c%c%d", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket); } //+------------------------------------------------------------------+
La partie la plus importante est mise en évidence. C’est elle qui modélise les noms des objets. Je conserve les éléments de base qui sont encore disponibles dans le système. Nous créons et modifions d'abord la modélisation, ce qui permet au système de rester stable. Nous ajouterons ensuite de nouveaux objets facilement et rapidement. Et en plus, nous maintiendrons la stabilité déjà atteinte.
Bien que le code ait subi beaucoup plus de changements que ceux présentés ici, je me concentrerai uniquement sur les nouvelles fonctions, ainsi que sur les changements qui ont été considérables par rapport aux versions précédentes.
La première fonction est celle-ci :
inline string CreateIndicatorTrade(ulong ticket, eIndicatorTrade it, bool select) { string sz0 = MountName(ticket, it); ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_COLOR, (it == IT_PRICE ? clrBlue : (it == IT_STOP ? clrFireBrick : clrForestGreen))); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_WIDTH, 1); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_STYLE, STYLE_DASHDOT); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTABLE, select); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTED, false); ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_BACK, true); ObjectSetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, (string)ticket + " "+StringSubstr(EnumToString(it), 3, 10)); return sz0; }
Pour l'instant, elle ne crée qu'une ligne horizontale. Faites attention au code de génération des noms. Notez également que les couleurs seront désormais définies en interne dans le code et non plus par l'utilisateur.
Nous surchargeons ensuite la même fonction :
inline string CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select) { if (price <= 0) { RemoveIndicatorTrade(ticket, it); return NULL; } string sz0 = CreateIndicatorTrade(ticket, it, select); ObjectMove(Terminal.Get_ID(), sz0, 0, 0, price); return sz0; }
Attention à ne pas confondre ces deux fonctions : elles semblent identiques mais sont différentes en réalité. La surcharge est assez courante : nous créons une fonction simple et lui ajoutons ensuite de nouveaux paramètres pour effectuer un certain type de modélisation. Si nous ne l'implémentons pas via une surcharge, nous devrons parfois répéter la même séquence de code. C'est dangereux, car on peut oublier de déclarer quelque chose. Ce n'est en plus pas très pratique. C'est pourquoi nous surchargeons la fonction pour qu'elle ne fasse qu'un seul appel au lieu de plusieurs.
Il convient de mentionner ici la partie surlignée dans cette deuxième version. Il n'est pas nécessaire de la créer ici. Nous pourrons le faire ailleurs. Mais comme on peut le voir, lorsque nous essayons de créer un objet avec un prix à 0, il doit en fait être détruit.
Pour voir le moment où cela se produit, jetez un coup d'œil à ce code :
class C_Router : public C_ObjectsTrade { // ... Internal class code .... void UpdatePosition(int iAdjust = -1) { // ... Internal function code ... for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol()) { ul = PositionGetInteger(POSITION_TICKET); m_bContainsPosition = true; CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false); CreateIndicatorTrade(ul, take = PositionGetDouble(POSITION_TP), IT_TAKE, true); CreateIndicatorTrade(ul, stop = PositionGetDouble(POSITION_SL), IT_STOP, true); // ... The rest of the code...
Chaque fois que l'EA reçoit l'événement OnTrade, il exécute la fonction ci-dessus et tente de créer un indicateur sur les points sélectionnés. Mais si l'utilisateur supprime la limite, celle-ci devient nulle. Par conséquent, lorsqu'elle est appelée, elle supprime l'indicateur du graphique, ce qui nous évite d'avoir des objets inutiles en mémoire. Nous avons donc un gain sur certains points, puisque le contrôle sera effectué au moment même de sa création.
Mais nous avons toujours un problème avec la surcharge, parce que certaines personnes peuvent ne pas comprendre pleinement comment elle est utilisée dans le code réel. Pour le comprendre, regardez les deux parties de code ci-dessous :
class C_OrderView : public C_Router { private : //+------------------------------------------------------------------+ public : //+------------------------------------------------------------------+ void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1) { SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1); CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false); CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false); CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false); } //+------------------------------------------------------------------+ // ... Rest of the code... class C_Router : public C_ObjectsTrade { // ... Class code ... void UpdatePosition(int iAdjust = -1) { // ... Function code .... for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol()) { ul = PositionGetInteger(POSITION_TICKET); m_bContainsPosition = true; CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false); // ... The rest of the code...
Notez que le nom de la fonction utilisée est le même dans les deux cas. Elles font également toutes deux partie de la même classe C_ObjectsTrade. Mais même dans ce cas, le compilateur peut les distinguer, en raison du nombre de paramètres. Si vous y regardez de plus près, vous verrez que la seule différence est un paramètre "prix" supplémentaire. Mais il peut y en avoir d'autres. Comme vous pouvez le constater, il est beaucoup plus facile d'utiliser un appel pour copier tout le code présent dans l'une des versions surchargées. Cela permet d'obtenir un code plus propre et plus facile à maintenir.
Revenons maintenant à la classe C_ObjectsTrade. Voici la fonction suivante que nous devons comprendre :
bool GetInfosOrder(const string &sparam, ulong &ticket, double &price, eIndicatorTrade &it) { string szRet[]; char szInfo[]; if (StringSplit(sparam, def_SeparatorInfo, szRet) < 2) return false; if (szRet[0] != def_NameObjectsTrade) return false; StringToCharArray(szRet[1], szInfo); it = (eIndicatorTrade)szInfo[0]; ticket = (ulong) StringToInteger(szRet[2]); price = ObjectGetDouble(Terminal.Get_ID(), sparam, OBJPROP_PRICE); return true; }
Il s'agit du cœur, de l'esprit et du corps de l'ensemble du nouveau système. Bien qu'il paraisse très simple, il accomplit une tâche essentielle pour que l'ensemble de l'EA fonctionne comme l'exige notre nouveau système de modélisation.
Faites attention au code mis en évidence, en particulier à la fonction StringSplit. Si elle n'existait pas dans le langage MQL5, nous devrions la coder. Heureusement, MQL5 la fournit. Nous allons donc pouvoir l’utiliser au maximum. Elle décompose le nom de l'objet en données nécessaires. Lorsqu'un nom d'objet est créé, il est modélisé d'une manière très spécifique. C'est pourquoi nous pouvons annuler ce modèle de codage, de sorte que StringSplit annulera ce que fait la fonction StringFormat.
Le reste de la fonction capture les données présentes dans le nom de l'objet pour que nous puissions le tester et l'utiliser ultérieurement. En d'autres termes, MetaTrader 5 génère les données pour nous, nous les décomposons afin de savoir ce qui s'est passé et nous donnons ensuite à MetaTrader 5 les mesures à prendre. Notre objectif est de faire en sorte que MetaTrader 5 fonctionne pour nous. Je ne crée pas un modèle à partir de zéro. Mais je modélise l'interface et l'EA à partir de zéro. Nous devrions donc bénéficier de l'assistance offerte par MetaTrader 5 au lieu de chercher une solution externe.
Dans le code ci-dessous, nous ferons quelque chose de très similaire à ce que nous avons fait plus haut :
inline void RemoveAllsIndicatorTrade(bool bFull) { string sz0, szRet[]; int i0 = StringLen(def_NameObjectsTrade); ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false); for (int c0 = ObjectsTotal(Terminal.Get_ID(), -1, -1); c0 >= 0; c0--) { sz0 = ObjectName(Terminal.Get_ID(), c0, -1, -1); if (StringSubstr(sz0, 0, i0) == def_NameObjectsTrade) { if (!bFull) { StringSplit(sz0, def_SeparatorInfo, szRet); if (StringToInteger(szRet[2]) == def_IndicatorTicket0) continue; } }else continue; ObjectDelete(Terminal.Get_ID(), sz0); } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); }
Chaque fois que nous supprimons une ligne du graphique, qu'il s'agisse d'une position qui sera fermée ou d'un niveau de limite qui sera supprimé, l'objet correspondant doit être supprimé, comme lorsque l'EA est supprimé du graphique. Nous devons supprimer les objets. Mais nous avons également un ensemble de lignes qui ne doivent pas être supprimées sauf en cas d'absolue nécessité : il s'agit de Ticket0, qui ne doit pas être supprimé, sauf en cas d'extrême nécessité. Pour éviter sa suppression, utilisons le code mis en évidence. Sans cela, nous devrions créer ce Ticket0 à chaque fois. Ce ticket est très important dans une autre partie du code que nous aborderons plus tard.
Dans tous les autres cas, nous devons supprimer quelque chose de spécifique. Pour ce faire, nous utiliserons une autre fonction de suppression présentée ci-dessous.
inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL) { ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false); if ((it != NULL) && (it != IT_PRICE)) ObjectDelete(Terminal.Get_ID(), MountName(ticket, it)); else { ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_PRICE)); ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_TAKE)); ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_STOP)); } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); }
La nouvelle routine est présentée ci-dessous :
inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy) { double ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal()); ObjectMove(Terminal.Get_ID(), MountName(ticket, it), 0, 0, price); if (it == IT_PRICE) { ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_TAKE), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad)))); ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_STOP), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad))); } }
Elle place les objets sur l'axe des prix. Mais ne vous y attachez pas trop, car elle cessera bientôt d'exister pour diverses raisons. Parmi elles, il y a celle que nous avions abordée dans un autre article de cette série : Plusieurs indicateurs sur un même graphique (Partie 05) : Conversion de MetaTrader 5 en système RAD(I). Cet article contient un tableau des objets pouvant utiliser des coordonnées cartésiennes (X et Y) pour le positionnement. Les coordonnées de prix et de temps, bien qu'utiles dans certains cas, ne sont pas toujours pratiques : lorsque nous voulons positionner des éléments qui doivent être placés à certains endroits de l'écran, bien qu'il soit plus rapide de développer des choses en utilisant les coordonnées de prix et de temps, elles sont beaucoup plus difficiles à travailler que le système X et Y.
Nous apporterons des changements la prochaine fois. Mais pour l'instant notre objectif est de créer un système alternatif à celui utilisé jusqu'à présent.
Nous avons ensuite la dernière fonction importante de la classe C_ObjectsTrade. C’est ce qui est indiqué dans la ligne suivante :
inline double GetDisplacement(const bool IsBuy, const double Vol, eIndicatorTrade it) const { int i0 = (it == IT_TAKE ? m_BaseFinance.FinanceTake : m_BaseFinance.FinanceStop), i1 = (it == IT_TAKE ? (IsBuy ? 1 : -1) : (IsBuy ? -1 : 1)); return (Terminal.AdjustPrice(i0 * (Vol / m_BaseFinance.Leverange) * Terminal.GetAdjustToTrade() / Vol) * i1); }
Cette fonction effectue la conversion entre les valeurs spécifiées dans Chart Trader pour un ordre en attente à placer ou pour une position qui sera ouverte sur le marché.
Tous ces changements ont été mis en œuvre pour transformer la fonction C_HLineTrade en C_ObjectsTrade. Malgré tout, ces changements ont également nécessité d'autres modifications. Par exemple, la classe C_ViewOrder a également beaucoup changé. Certaines parties de cette classe ont tout simplement été supprimées parce qu'elles n'avaient plus de raison d'être. Et les fonctions restantes ont été modifiées. Les fonctions qui méritent une attention particulière sont mises en évidence ci-dessous.
La première est la fonction d'initialisation des données provenant de Chart Trader :
void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1) { SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1); CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false); CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false); CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false); }
Les parties surlignées sont celles où Ticket0 est créé. Ce ticket est utilisé pour placer un ordre en attente à l'aide de la souris et du clavier : SHIFT pour acheter, CTRL pour vendre. Auparavant, des lignes étaient créées à ces endroits, qui étaient ensuite utilisées pour indiquer l'emplacement de l'ordre. Les choses sont maintenant beaucoup plus simples : de la même manière que nous voyons un ordre à placer, nous verrons également un ordre en attente ou une position ouverte. Cela signifie que nous contrôlerons toujours le système. C'est comme si vous assembliez un véhicule et que vous vérifiiez en permanence ses freins afin de savoir comment ils se comporteront lorsque vous devrez les utiliser.
Le gros problème d'un code long est que lorsque nous créons une fonction, nous ne pouvons savoir qu'elle fonctionne qu'au moment où elle est effectivement utilisée. Mais aujourd'hui, le système est toujours vérifié : même si nous n'utilisons pas toutes les fonctions, elles sont constamment vérifiées en raison de la réutilisation du code à différents endroits.
La dernière fonction que je mentionnerai dans cet article est illustrée ci-dessous. Elle placera un ordre en attente. Notez qu'elle est devenue extrêmement compacte par rapport à la même fonction des articles précédents :
inline void MoveTo(uint Key) { static double local = 0; datetime dt; bool bEClick, bKeyBuy, bKeySell, bCheck; double take = 0, stop = 0, price; bEClick = (Key & 0x01) == 0x01; //Let mouse button click bKeyBuy = (Key & 0x04) == 0x04; //Pressed SHIFT bKeySell = (Key & 0x08) == 0x08; //Pressed CTRL Mouse.GetPositionDP(dt, price); if (bKeyBuy != bKeySell) { Mouse.Hide(); bCheck = CheckLimits(price); } else Mouse.Show(); PositionAxlePrice((bKeyBuy != bKeySell ? price : 0), def_IndicatorTicket0, IT_PRICE, (bCheck ? 0 : GetBaseFinanceTakeProfit()), (bCheck ? 0 : GetBaseFinanceStopLoss()), GetBaseFinanceLeveRange(), bKeyBuy); if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, local = price); local = (local != price ? 0 : local); }
La raison en est qu'il y aura désormais une nouvelle règle dans le système, de sorte que la fonction a "perdu du poids" et est devenue plus compacte.
Conclusion
J'ai présenté ici quelques modifications qui seront utilisées dans le prochain article. Le but de tout cela est de les simplifier et de montrer des choses qui peuvent être différentes à différents moments. Mon idée est que chacun suive et apprenne à programmer un EA qui sera utilisé pour vous aider dans vos opérations. C'est pourquoi je ne présente pas simplement un système fini et prêt à l'emploi. Je veux montrer qu'il y a des problèmes à résoudre et présenter la voie que j'ai suivie pour résoudre les questions et les problèmes qui peuvent se poser au cours du développement. J'espère que vous comprenez ce point. Car si l'idée était de créer un système et de le présenter sous une forme prête à l'emploi, je ferais mieux de le faire et de vendre l'idée. Mais ce n'est pas mon intention...
Traduit du portugais par MetaQuotes Ltd.
Article original : https://www.mql5.com/pt/articles/10474
- Applications de trading gratuites
- VPS Forex gratuit pendant 24 heures
- 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