L'utilisation de ORDER_MAGIC pour trader avec différents Expert Advisors sur un seul instrument
Introduction
Dans MQL5, nous avons la possibilité d’attribuer un nombre magique à chaque ordre en attente, afin d’utiliser ces informations dans l’identification de la commande. Cela ouvre les vastes possibilités d’interaction entre les différents Expert Advisors et le développement de systèmes encore plus complexes. Dans cet article, je voudrais informer le public sur les opportunités sous-estimées du nombre Magique.
Mais avant de passer à l’essence du sujet de cet article, nous devons mieux comprendre ce qui constitue le nombre magique. Qu’est-ce qui pourrait être magique dans un nombre, qui détermine quel Expert Advisor l’a défini ? Les "miracles" commencent par les opportunités, que les développeurs définissent dans le type ulong, qui est déclaré par le nombre magique.
Le type ulong est le plus long
Si nous observons en détail le type entier long, nous constatons que la valeur maximale de ce type est tout simplement phénoménale:
Type | Taille en Octets | Valeur Minimum | Valeur Maximum | Analogique dans le langage C + + |
long | 8 | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 | __int64 |
ulong | 8 | 0 | 18 446 744 073 709 551 615 | unsigned __int64 |
Tableau 1. Propriétés des types de données long et ulong
mais le type ulong l’a surpassé en combinant la mantisse positive et négative.
Donc, la longueur indiquée est énorme, mais comment était-elle utilisée auparavant?
De par mon expérience de travail en mql 4, j’ai souvent remarqué l’absurdité du codage du nombre magique par de nombreux développeurs. Le nombre magique a été utilisé judicieusement, mais son codage semblait assez stupide. Ce que l’on peut dire de l’individualité du nombre magique 12345, une telle Magie est utilisée par près de la moitié de la confrérie en développement. La deuxième moitié utilise les nombres magiques 55555, 33333 et 77777, et c’est à peu près l’ensemble complet. Je veux attirer l’attention du lecteur sur le fait qu’il est peu probable que son ordinateur compte plus de 1 000 Expert Advisors, donc le nombre 1000 sera suffisant pour chiffrer le nom individuel de tous vos Expert Advisors.
1000 - n’est que 3 catégories complètes, alors que devrions-nous faire avec les 15 catégories complètes restantes, qui sont disponibles dans le type ulong? La réponse est simple : Chiffrez-les
Que dit Wikipidia à propos du mot code :
Le Code - règle (algorithme) la comparaison pour chaque message individuel d’une combinaison strictement spéciale symboles (caractères) (or signaux).
Par conséquent, nous allons établir les règles. Je propose de prescrire dans le code du nombre Magique, non seulement l’ID de l’Expert Advisor, mais aussi l’instrument sur lequel il fonctionne. Le fait que l’Expert Advisor fonctionne, par exemple, sur EURUSD, ne signifie pas que l’Expert Advisor affichera un ordre uniquement sur cet instrument. Aussi, je pense qu’il sera utile d’écrire le code d’interaction des Expert Advisors, quelque chose comme « le vôtre / étranger », afin que l’Expert Advisor, lors de la vérification des positions, puisse comprendre que l’ordre actuel est établi par un Expert Advisor amical. Je pense que ce sera suffisant pour élaborer un système très complexe.
Et résumons donc ce que nous avons: quelles opportunités mettons-nous dans le système:
- La possibilité de deux Expert Advisors ou plus de travailler sur un seul instrument et de ne pas interférer.
- La possibilité pour deux Expert Advisors ou plus de travailler sur des instruments différents et de se compléter.
- La capacité d’identifier l’ordre par l’instrument, en travaillant avec l’Expert Advisor.
Et donc, la tâche est définie, entamons dés à présent son implémentation.
Simple Expert Advisor
Rédigez le code de l’Expert Advisor simple - par exemple, maintenez la position dans la direction du Déplacement. Je pense que le lecteur, qui a décidé d’analyser le nombre magique, a déjà lu l’article Guide, étape par étape pour écrire un Expert Advisor dans MQL5 pour débutants, sinon, je recommande vivement de le faire, car je n’entrerai pas dans les détails de la création de l’Expert Advisor. En principe, l’Expert Advisor ouvrira la position une fois et la tournera pour toutes les autres fois. Par conséquent, nous aurons besoin de la fonction d’ouverture de la position, c’est-à-dire de placer la demande de trading (commande de trading).
Créez une classe auxiliaire, qui calculera les paramètres pour nous pour remplir les champs de la structure de demande de trading.
//+------------------------------------------------------------------+ //| The class provides auxiliary trading calculations | //+------------------------------------------------------------------+ class CProvision { protected: MqlTradeRequest trades; // pointer to the request structure of OrderSend public: int TYPE(const double &v[]); // determines the type, in respect to the readings of the moving double pricetype(int type); // calculates the level of the opening, in respect to the type double SLtype(int type); // calculates the level of the stop-loss in respect to the type double TPtype(int type); // calculates the level of the take-profit, in respect to the type long spread(); // returns the spread of the current instrument int SendOrder(ENUM_ORDER_TYPE type,double volume); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume) { trades.action =TRADE_ACTION_DEAL; // Type of the implemented actions trades.magic =magic; // Stamp of the Expert Advisor (identifier of the magic number) trades.symbol =_Symbol; // Name of the trading instrument trades.volume =volume; // Request the volume of the trade in lots trades.price =pricetype((int)type); // Price trades.sl =SLtype((int)type); // Level of Stop Loss order trades.tp =TPtype((int)type); // Level of Take Profit order trades.deviation=(int)spread(); // Maximum acceptable deviation from the requested price trades.type=type; // Order type trades.type_filling=ORDER_FILLING_FOK; if(OrderSend(trades,res)){return(res.retcode);} return(-1); } //+------------------------------------------------------------------+ //| Determines the type, in respect to the reading of the moving | //+------------------------------------------------------------------+ int CProvision::TYPE(const double &v[]) { double t=v[0]-v[1]; if(t==0.0)t=1.0; return((int)(0.5*t/fabs(t)+0.5)); } //+------------------------------------------------------------------+ //| Calculates the level of opening in respect to the type | //+------------------------------------------------------------------+ double CProvision::pricetype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.ask); if(type==1)return(tick.bid); } return(-1); } //+------------------------------------------------------------------+ //| Calculates the level of stop-loss in respect to the type | //+------------------------------------------------------------------+ double CProvision::SLtype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); } return(0); } //+------------------------------------------------------------------+ //| Calculates the level of timeframe in respect to the type | //+------------------------------------------------------------------+ double CProvision::TPtype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); } return(0); } //+------------------------------------------------------------------+ //| Returns the spread | //+------------------------------------------------------------------+ long CProvision::spread() { return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)); }
Ayant une telle classe, nous pouvons écrire un code pour un simple Expert Advisor sans problème:
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- Input parameters input ulong magic =1; // magic input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.1; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- We will store the indicator's handle int MA_handle, // Handle of the indicator type_MA, // Type that specify the direction of MA rezult; // The variable takes the value of the result of the OrderSend operation double v[2]; // Buffer for receiving values of MA MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CProvision prov; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication if(PositionSelect(_Symbol))// If there is an open position { if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close { Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME), " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot); // reverse the position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME), " open position of type ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot); // open position if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Exécutez-le et assurez-vous que l’Expert Advisor ne diffère pas en rentabilité, mais trade exactement conformément à la logique indiquée, ce qui est précisément ce dont nous avons besoin.
Figure 1. Le travail d’un Expert Advisor sur un seul instrument
Maintenant, nous allons essayer d’exécuter cet EA, mais sur plusieurs délais d’un instrument (pour les expériences, nous avons choisi un instrument aléatoire, qui est EURUSD: o)
Figure 2. Le conflit de deux Expert Advisors sur le même instrument sur des délais différents
Étant donné que les deux Expert Advisors fonctionnent sur un seul instrument et que le code n’indique pas le partage des positions, donc les deux Expert Advisors tentent de corriger la position de trading, en fonction des lectures de leurs indicateurs, et par conséquent, - un conflit surgit. L’Expert Advisor qui fonctionne sur M1, tente de faire tourner la position dans la Cellule, tandis que son rival cherche à l’arrêter. Il est évident que nous avons besoin d’un calcul séparé des positions, ce que nous allons faire maintenant.
Position ou position virtuelle ?
Étant donné que dans MetaTrader 5, les développeurs sont passés des ordres à la prise en compte des positions, il est logique d’examiner plus en détail les fonctions associées à des positions d’enregistrement.
// Returns the number of open positions. int PositionsTotal(); // Returns the symbol of the open position by the number in the list of positions. string PositionGetSymbol(int index); // Selects the open position for further working with it. bool PositionSelect(string symbol, uint timeout=0); // Function returns the requested property of the open position. double PositionGetDouble(ENUM_POSITION_PROPERTY property_id); // The function returns the requested property of the open position. long PositionGetInteger(ENUM_POSITION_PROPERTY property_id); // The function returns the requested property of the open position. string PositionGetString(ENUM_POSITION_PROPERTY property_id);
Identificateurs des énumérations pour les fonctions d’obtention de la demande des propriétés de position conformes PositionGetDouble, PositionGetInteger, et PositionGetString qui sont indiquées dans les tableaux 2-4.
Identifiant | Description | Type |
POSITION_VOLUME | Volume d'une position | double |
POSITION_PRIX_OUVERT | Prix de la position | double |
POSITION_SL | Niveau d’Excédent de Pertes pour la position ouverte | double |
POSITION_TP | Niveau de Prise de Bénéfices pour la position ouverte | double |
POSITION_PRIX_ACTUEL | Prix actuel par le symbole | double |
POSITION_COMMISSION | Commission | double |
POSITION_ÉCHANGE | Échange accumulé | double |
POSITION_BÉNÉFICE | Bénéfice actuel | double |
Tableau 2. ENUM_POSITION_PROPERTY_DOUBLE de valeur d’énumération
Identifiant | Description | Type |
POSITION_TEMPS | Heure d’ouverture des postes | date/heure |
POSITION_TYPE | Type de poste | |
POSITION_MAGIC | Nombre magique pour le poste (voir ORDER_MAGIC ) | long |
POSITION_IDENTIFIER | Identification de la position - il s’agit d’un numéro unique qui est attribué à chaque position rouverte et qui ne change pas tout au long de son cycle de vie. Le chiffre d’affaires d’une position ne change pas son identifiant. | long |
Tableau 3. Valeurs d’énumération ENUM_POSITION_PROPERTY_INTEGER
Identifiant | Description | Type |
POSITION_SYMBOL | Symbole, pour lequel la position est ouverte | chaîne |
POSITION_COMMENT | Commentaire de la position | chaîne |
Tableau 4. Valeurs d’énumération ENUM_POSITION_PROPERTY_STRING
D’après les fonctions, nous pouvons clairement constater que le langage ne comporte pas la division des positions, basée sur le principe de « qui a mis l’ordre », mais la possibilité de tels enregistrements est disponible puisque les ORDER_MAGIC, les POSITION_MAGIC et les DEAL_MAGIC sont le même nombre exact, et sont tirés du nombre magique, indiqué par l’utilisateur. La POSITION_MAGIC est prise à partir de la DEAL_MAGIC, qui ouvre la position, et la DEAL_MAGIC, à son tour, est prise à partir de la ORDER_MAGIC, de la commande passée.
Identifier une commande, une transaction ou une position peut se faire sans problème, mais il est impossible de diriger une position par un nombre magique particulier. Maintenant, nous allons essayer d’éliminer cette carence . élaborons des analogues de fonctions intégrées, mais avec identification par le nombre magique. Déclarez une classe pour travailler avec une position virtuelle sur le nombre Magic.
Puisque nous avons la possibilité de travailler avec la POO, déclarons également notre propre structure (en acquérant une pratique supplémentaire de l’écriture objective).
//+------------------------------------------------------------------+ //| Structure of the CPositionVirtualMagic class | //+------------------------------------------------------------------+ struct SPositionVirtualMagic { double volume; // volume of virt. position ENUM_POSITION_TYPE type; // type of virt. position }; //+--------------------------------------------------------------------------------+ //| The class calculates the virtual position of an Expert Advisor by magic number | //+--------------------------------------------------------------------------------+ class CPositionVirtualMagic { protected: SPositionVirtualMagic pvm; public: double cVOLUME(){return(pvm.volume);} // Returns the volume of virtual position of an Expert Advisor ENUM_POSITION_TYPE cTYPE(){return(pvm.type);} // Returns the type of virtual position of an Expert Advisor bool PositionVirtualMagic(ulong Magic, string symbol, datetime CurrentTime ); // the method of calculation virt. position returns the presence or absence of virt. position private: void prHistory_Deals(ulong &buf[],int HTD); // Fills the array of tickets }; //+-------------------------------------------------------------------------------------+ //| Method of calculation of virt. position, returns true if there is a virt. position | //+-------------------------------------------------------------------------------------+ bool CPositionVirtualMagic::PositionVirtualMagic(ulong Magic, string symbol, datetime CurrentTime ) { int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0; ulong Dticket=0; int History_Total_Deals=-1; double volume=0,volume_BUY=0,volume_SELL=0; ulong DTicketbuf[]; do { if(HistorySelect(0,TimeCurrent())) { History_Total_Deals=HistoryDealsTotal(); prHistory_Deals(DTicketbuf,History_Total_Deals); } HistorySelect(0,TimeCurrent()); } while(History_Total_Deals!=HistoryDealsTotal()); for(int t=0;t<History_Total_Deals;t++) { Dticket=DTicketbuf[t]; if(HistoryDealSelect(Dticket)) { if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime) { if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic) { if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY) { volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME); } else { if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL) { volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME); } } } } } else{HistorySelect(0,TimeCurrent());t--;} // if there is a fault, load history data and pass the step again } volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS); if(volume<0)pvm.type=POSITION_TYPE_SELL; else { if(volume>0)pvm.type=POSITION_TYPE_BUY; } pvm.volume=fabs(volume); if(pvm.volume==0)return(false); else return(true); }
Dans le texte ci-dessus (où le code de la classe CProvision est indiqué), il n’a pas été expliqué d’où tout vient et où cela va plus loin, puisque le développement de l’Expert Advisor n’est pas l’objet de cet article.
Mais nous allons considérer en détail la classe CPositionVirtualMagic.
La structure bénéficie de la classe:
struct SPositionVirtualMagic
qui est utilisée pour accepter les résultats des calculs, une telle déclaration globale au sein de la classe, grâce à pvm (variable de la structure), cette structure sera disponible partout, dans n’importe quelle méthode de la classe.
Suivez ensuite les deux méthodes de la classe :
double cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA ENUM_POSITION_TYPE cTYPE() {return(pvm.type);} // Returns the type of the virtual position of the EA
Ces méthodes sont déclarées publiques et, par conséquent, elles seront disponibles via la variable de classe appelée, à n’importe quel endroit du programme, et sont conçues pour la sortie des valeurs de structure à l’emplacement demandé.
Il s’agit également de la section où la méthode suivante est déclarée :
bool PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);
C’est la fonction principale de la classe, et nous nous concentrerons davantage sur ses analyses détaillées, et en attendant, je vais me présenter devant moi-même et décrire la fonction conformément au spécificateur de l’accès privé:
void prHistory_Deals(ulong &buf[],int HTD);
Cette méthode produit un enregistrement de ticket des transactions dans le tableau, qui est essentiellement un cycle, et pourrait être décrit dans la fonction appelée, mais je voulais réduire la taille (afin d’augmenter la lisibilité du code) de la fonction PositionVirtualMagic(), et j’ai donc déplacé ce cycle au-delà des limites de la fonction, et j’ai donc démontré comment utiliser le spécificateur de l’accès privé.
Revenons donc à positionVirtualMagic() . Cette fonction, à son tout début, a un calcul de précision d’une ligne, auquel vous devez arrondir la double valeur du volume de la position calculée.
int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;
Ceci est nécessaire pour faire l’opération de comparaison à zéro, sinon un certain équilibre dans les 8ème chiffres après la virgule, nous empêchera d’assimiler la valeur à zéro et entraînera une erreur d’exécution.
Le volume de position est arrondi à l’étape minimale. Et si l’étape minimale est supérieure à 1, l’arrondi est effectué par la partie intégrale. Vient ensuite le cycle pendant, mais il est utilisé d’une nouvelle manière (différente de celle de mql4), puisque la vérification de l’expression véridique se fait plutôt à la fin qu’au début du cycle :
do { if(HistorySelect(0,TimeCurrent())) { History_Total_Deals=HistoryDealsTotal(); prHistory_Deals(DTicketbuf,History_Total_Deals); } HistorySelect(0,TimeCurrent()); } while(History_Total_Deals!=HistoryDealsTotal());
Une telle approche est appliquée parce que l’expression de la véracité est calculée dans le cycle, et à son lancement, elle n’est pas encore préparée pour cette vérification.
Le cycle contient le téléchargement de l’historique , je veux attirer l’attention du lecteur sur le fait que c’est une condition requise afin de s’assurer que le travail des fonctions intégrées de travailler avec l’historique.
HistorySelect(0,TimeCurrent())
Je pense que je devrais expliquer mon système de choix des noms de variables.
Le lecteur attentif aurait dû remarquer que les noms des classes sont définis par la lettre initiale « C », ce n’est pas requis par la syntaxe et n’importe quel nom peut être attribué, mais une telle manière rend la lecture beaucoup plus facile. Si la lettre « C » apparaît avant le nom, nous nous rendons compte tout de suite que c’est le nom de la classe, et si la lettre est « S » - alors il s’agit une structure. Si la variable prend la valeur d’une fonction intégrée, alors je change simplement les composants du nom de la fonction et j’obtiens le nom de la variable, par exemple comme ceci:
CurrentTime = TimeCurrent();
Simple et lisible, on peut immédiatement constater ce que contient la variable. D’autant plus que MetaEditor contient la fonction permettant de faire glisser un élément de code particulier dans un emplacement spécifié.
En examinant davantage le code nous constatons qu’après le téléchargement de l’historique, suit l’appel à la fonction:
History_Total_Deals=HistoryDealsTotal();
avec le stockage du nombre de transactions dans la variable. À la même condition, nous implémentons la vérification de la sortie du cycle. Pour quoi avons-nous besoin de cette vérification? Et pourquoi ne pouvons-nous pas simplement télécharger l’historique, puis récupérer les transactions à partir de celui-ci?
Le problème réside dans le fait que pendant le travail des Expert Advisors, l’historique sera séparément demandé par chaque EA, et donc si les Expert Advisors fonctionnent à des moments différents,alors la profondeur de l’historique sera différente. Et cela signifie que lorsqu’un Expert Advisor entre dans le cycle et télécharge l’historique pour sa période, avant d’atteindre la fin du cycle, il peut découvrir que cet historique a déjà été téléchargé à la demande d’un autre Expert Advisor, et donc une vérification de l’authenticité doit être faite.
Incidemment, ce n’est peut-être pas le meilleur type de vérification, mais cela fonctionne. Et donc, poursuivons. Dans le cycle, nous appelons la méthode de classe, qui saisit les valeurs de ticket des transactions dans un tampon spécialement préparé. Après avoir appelé la fonction prHistory_Deals (), nous produisons à nouveau le téléchargement de l’historique.
C’est ainsi que l’on vérifie s’il y a eu ou non des changements dans l’historique des trades, au cours des travaux de la fonction prHistory_Deals (). S’il n’y a pas eu de modifications, la variable de History_Total_Deals sera donc égale à HistoryDealsTotal (), et une sortie du cycle à passage unique se produira. S’il y a eu des changements, le système lancera un deuxième cycle et continuera à répéter jusqu’à ce que l’historique des tickets soit téléchargé sans aucune erreur (et n’oubliez pas de mettre « ; » à la fin) :
while(History_Total_Deals!=HistoryDealsTotal());
Plus loin dans le cycle pour, le calcul des positions virtuelles intervient.
Si la transaction a passé avec succès une série de filtres (l’heure de la transaction et le numéro magique de la transaction),puis son volume augmente la partie de la position virtuelle, le type auquel appartient la transaction.
Je tiens à noter que je n’enregistre les calculs des positions virtuelles qu’à partir du lancement de l’Expert Advisor, bien que d’autres options soient envisageables.
Ici, il convient de noter comment exactement la position est calculée. Par le registre, que nous utilisons tous depuis des temps immémoriaux et à ce jour, nous avons des dépenses et des bénéfices, et le comptage du solde est conservé comme la différence entre ces valeurs, le même schéma s’applique au calcul d’une position: si vous ouvrez des lots, 0,2 à Vendre et 0,3 à Acheter, cela signifie que vous détenez la position de 0,1 pour Acheter. Le moment de l’ouverture et la différence de niveaux sont des catégories de profit, mais la position que vous occuperez est de 0,1 lot, le type d’Achat.
C’est pourquoi nous résumons simplement toutes les transactions effectuées par l’Expert Advisor sur Acheter et séparément, sur Vendre, puis nous les comparons et obtenons la position générale (en fait, c’est ce avec quoi le reste de la fonction examinée est engagée).
Calcul du volume de positions :
volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
Identification du type de position avec la sortie de la valeur dans la structure:
if(volume<0)pvm.type=POSITION_TYPE_SELL; else { if(volume>0)pvm.type=POSITION_TYPE_BUY; }
Sortie du volume dans la structure:
pvm.volume=fabs(volume);
La sortie de la valeur de la fonction: si le volume de position est 0, alors il est faux, sinon, si la position existe, alors il est vrai:
if(pvm.volume==0)return(false); else return(true);
Maintenant, ayant la fonction de la position virtuelle, nous pouvons facilement établir le code de l’Expert Advisor, qui n’entrera pas en conflit avec ses « voisins ».
Pour gagner de l’espace, je vais fournir certaines parties du code, qui n’ont pas été présentées ci-dessus, plutôt que le code entier lui-même.
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- input parameters input ulong magic =1; // magic input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.1; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- we will store the indicator's handle int MA_handle,type_MA,rezult; double v[2]; datetime CurrentTime; // The variable stores the time of start of the Expert Advisor MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CPositionVirtualMagic cpvm; CProvision prov; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position { if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close { Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(), " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA); //cpvm.cVOLUME() - volume of virtual position rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Exécutez l’Expert Advisor trois fois sur un seul instrument, mais avec des délais différents, et également attribuez des nombres magiques différents à chaque fois :
Figure 3. Attribuons des nombres magiques différents à deux Expert Advisors identiques, (un instrument, des délais différents) lancement du premier Expert Advisor
Figure 4. Attribuons des nombres magiques différents à deux Expert Advisors identiques (un instrument, des délais différents) lancement du deuxième Expert Advisor
Figure 5. Le résultat est un travail non conflictuel d’Expert Advisors sur un seul instrument, avec divers nombres magiques
L’essai a été réalisé avec succès, les Expert Advisors cèdent obligeamment la place les uns aux autres, et aucune question conflictuelle ne semble être présente.
Le premier point du Cahier des Charges a été implémenté, mais il y a plus à venir.
Coder la magie
Pour l’implémentation des parties suivantes, nous devrons élaborer une classe de méthodes, qui encodera / décodera les informations, ainsi que récupérer les valeurs des fonctions intégrées et les transformer dans un format spécifié.
Pour ce faire, répétez les termes du codage (pour ainsi dire, le cahier des charges pour élaboration):
- Les méthodes doivent coder le nom de l’Expert Advisor (appelons-le le nom numérique),
- Code d’identification du vôtre / étranger (appelons-le le code d’interaction)
- Code de symbole, sur lequel l’Expert Advisor s’exécute (afin de pouvoir déterminer depuis la transaction à partir de laquelle l’EA travaille).
Pour commencer, sélectionnons le nom de la nouvelle classe, - que ce soit magique (un nom générique), assignons notre énumération, pour rendre le code plus compréhensible visuellement.
enum Emagic { ENUM_DIGITAL_NAME, // digital name if the Expert Advisor ENUM_CODE_INTERACTION,// code of interaction ENUM_EXPERT_SYMBOL // symbol, on which the EA is launched };
L’énumération fonctionne simplement : vous décrivez les noms, séparés par des virgules, et le compilateur leur attribue des numéros par l’enchainement.
Tout d’abord, si vous attribuez une variable d’un type différent (cela ne s’applique pas aux nombres), alors lors de la spécification d’un paramètre de l’énumération, vous recevrez une erreur lors de la compilation, et deuxièmement, vous obtenez de la clarté: vous n’attribuez pas simplement 0 , mais donnez plutôt la commande pour attribuer ENUM_DIGITAL_NAME .
Comme pour l’élaboration d’une structure ou d’une classe, j’ai choisi un nom simple pour l’énumération. J’ai simplement ajouté E au nom généralement sélectionné, et obtenu Emagic , respectivement, la structure correspondante sera Smagic , et la classe Cmagic .
Encore une fois, faites attention que cette question n’est pas obligatoire et que vous pouvez appeler l’énumérateur de l’énumération, le structureur de structure et le classificateur de classe. Mais cela ne fournira pas d’uniformité dans les noms, et la lecture de ce type de code sera inconfortable.
Ensuite, créons une structure pour stocker nos codes.
struct Smagic { ulong magicnumber; // magic in an assembled form - how it is written in the order int digital_name; // digital name int code_interaction; // code of interaction int expert_symbol; // symbol, on which the Expert Advisor is launched };
Après cela, déclarez la classe Cmagic, dans laquelle nous enregistrons toutes les méthodes de codage et de décodage du Magic, y compris les méthodes de l’Expert Advisor précédent (il suffit de les déclarer dans la classe actuelle et de réécrire les en-têtes)
class Cmagic { protected: Smagic mag; SPositionVirtualMagic pvm; public: // the function returns the assembled magic, assembled from the incoming data ulong SetMagic_request(int digital_name=0,int code_interaction=0); // the function obtains the assembled magic and divides it according to the assembly logic ulong SetMagic_result(ulong magicnumber); // the function obtains the return identification and returns the requested part of the assembled magic ulong GetMagic_result(Emagic enum_); // the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic string sGetMagic_result(Emagic enum_); // returns the voulme of the virtual position of the Expert Advisor double cVOLUME(){return(pvm.volume);} // returns the type of the virtual position of the Expert Advisor ENUM_POSITION_TYPE cTYPE(){return(pvm.type);} // method of calculating the virtual position, returns the presence of absence of the virtual position bool PositionVirtualMagic(Emagic enum_, string symbol, datetime CurrentTime); private: // function divides the magic into three parts of three charges, and returns the part to which the category points to int decodeMagic_result(int category); // interpretor of instrument symbols into the digital code int symbolexpert(); // interpretor of the digital code into the prescribed text (Expert Advisors) string expertcode(int code); // interpretor of the digital code into the prescribed text (interaction) string codeinterdescript(int code); // interpretor of the digital code into the instrument symbol string symbolexpert(int code); // cycle of recording tickets into the buffer void prHistory_Deals(ulong &buf[],int HTD); };
Maintenant, nous allons élaborer les méthodes.
La première méthode de la classe :
//+------------------------------------------------------------------+ //| Function returns the assembled magic, assembled from the input data | //+------------------------------------------------------------------+ ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0) { if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)"); if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)"); mag.digital_name =digital_name; mag.code_interaction =code_interaction; mag.expert_symbol =symbolexpert(); mag.magicnumber =mag.digital_name*(int)pow(1000,2)+ mag.code_interaction*(int)pow(1000,1)+ mag.expert_symbol; return(mag.magicnumber); }
Cette méthode reçoit deux valeurs : un nom numérique de l’Expert Advisor et le code d’interaction.
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
Et vérifie immédiatement leur exactitude :
if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)"); if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");
Mais il n’y a pas de représailles contre les actions de l’utilisateur, même en cas d’erreur, cela continue docilement à fonctionner.
Vient ensuite l’affectation dans la structure des données d’entrée, que l’utilisateur indique, mais le symbole de l’instrument n’est pas indiqué et est obtenu à partir de la méthode privée:
int Cmagic::symbolexpert()
Je ne fournirai pas son code, car il est très long et est donné dans le fichier joint. Permettez-moi simplement de dire que cette méthode n’est fondamentalement qu’un tableau, qui attribue à chaque symbole de la fenêtre « vue du marché » un nombre correspondant: pour EURUSD, par exemple, c’est 1, etc.
Vous pouvez certainement obtenir ces données de manière dynamique, en écrivant un code pour une étude sur les devises présentes dans la fenêtre « vue du marché », mais la solution doit correspondre à la complexité du problème, et cela n’a aucun sens de traiter l’appel des fenêtres, nous le ferons donc de manière simple, - composez une liste de devises et attribuez à chacune d’entre elles un index.
Et, enfin, la ligne la plus importante de toute la méthode:
mag.magicnumber =mag.digital_name*(int)pow(1000,2)+ mag.code_interaction*(int)pow(1000,1)+ mag.expert_symbol;
assemblé à partir des parties disparates de l’ensemble de la Magie. C’est la Magie qui sera affectée à l’ordre de notre Expert Advisor.
La méthode publique suivante de la classe :
//+------------------------------------------------------------------+ //| Function obtains the assembled magic | //| and divides it according to the logic of the assembly | //+------------------------------------------------------------------+ ulong Cmagic::SetMagic_result(ulong magicnumber) { mag.magicnumber =magicnumber; mag.expert_symbol =decodeMagic_result(1); mag.code_interaction =decodeMagic_result(2); mag.digital_name =decodeMagic_result(3); return(mag.magicnumber); }
En fait, cette méthode sert de coquille, distribuant à travers la structure les résultats de trois appels d’une seule méthode privée. La déclaration sous ce spécificateur est bonne dans le fait qu’ils ne sont pas affichés dans le message d’invite contextuel, lorsque vous appelez une variable de classe, donnant l’impression que tout le travail a été effectué par une fonction publique.
Mais revenons à nos fonctions privées :
//+------------------------------------------------------------------+ //| Function divides the magic into three parts of three charges | //| and returns the part, which the category points to | //+------------------------------------------------------------------+ int Cmagic::decodeMagic_result(int category) { string string_value=(string)mag.magicnumber; int rem=(int)MathMod(StringLen(string_value),3); if(rem!=0) { rem=3-rem; string srem="0"; if(rem==2)srem="00"; string_value=srem+string_value; } int start_pos=StringLen(string_value)-3*category; string value=StringSubstr(string_value,start_pos,3); return((int)StringToInteger(value)); }
Visuellement, cette méthode peut être représentée comme une lecture d’un nombre à trois chiffres à partir du champ indiqué, par exemple, si nous avons un Magic 123456789 , nous pouvons le représenter comme | 123 | 456 | 789 | si le champ indiqué est 1 , le résultat sera 789 puisque les champs sont numérotés de droite à gauche.
Ainsi, après avoir utilisé les trois champs de ladite méthode , nous distribuons toutes les données acquises vers la structure. Cela se fait par une procédure de faire basculer la Magie à une chaîne minuscule de type :
string string_value=(string)mag.magicnumber;
suivi du tri des composants de ligne individuels.
Suivent ensuite deux fonctions similaires, qui sont dans leur essence des commutateurs de commutateurs, et ne diffèrent que par le type des valeurs de sortie:
//+------------------------------------------------------------------+ //| Function obtains the identifier of the return | //| and returns the requested part of the assembled magic | //+------------------------------------------------------------------+ ulong Cmagic::GetMagic_result(Emagic enum_) { switch(enum_) { case ENUM_DIGITAL_NAME : return(mag.digital_name); break; case ENUM_CODE_INTERACTION : return(mag.code_interaction); break; case ENUM_EXPERT_SYMBOL : return(mag.expert_symbol); break; default: return(mag.magicnumber); break; } } //+------------------------------------------------------------------------------+ //| Function obtains the identifier of the return and returns | //| a textual interpretation of the requested type of the assembled magic | //+------------------------------------------------------------------------------+ string Cmagic::sGetMagic_result(Emagic enum_) { switch(enum_) { case ENUM_DIGITAL_NAME : return(expertcode(mag.digital_name)); break; case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break; case ENUM_EXPERT_SYMBOL : return(symbolexpert(mag.expert_symbol)); break; default: return((string)mag.magicnumber); break; } }
Les fonctions renvoient la partie de la Magie, qui indique le paramètre de type Emagic, la première donnant le résultat sous la forme de ulong, qui est utilisé dans les calculs, et la seconde donnant les résultats de type chaîne, qui peut être utilisée pour la visualisation.
Dans la fonction GetMagic_result () tout est organisé simplement, il distribue les valeurs de la structure à travers le commutateur, de branches , tandis que sGetMagic_result () est un peu plus compliqué. Chaque cas de branche appelle une fonction de table, qui transmet la valeur de la structure sous une forme visuelle. Ainsi, si la valeur mag.expert_symbol , = 1, alors la première fonction donnera 1 , et la seconde EURUSD .
J’ai déjà expliqué les avantages des fonctions de table dans le codage / décodage de l’information, donc je mentionnerai seulement que chaque cas doit être considéré séparément, en fonction de la complexité de l’implémentation d’une méthode sans tableau, et de ses avantages par rapport au temps nécessaire pour rédiger les tableaux. S’il est plus facile d’écrire une tableau des états, il n’est pas nécessaire de compliquer les choses. Mais si la rédaction du tableau prend beaucoup de temps, alors, évidemment, la préférence devrait être accordée aux méthodes procédurales. Je ne fournit pas les tableaux afin de gagner de l’espace (ils peuvent être trouvés dans les fichiers joints).
Fondamentalement, c’est ça, notre classe est élaborée, cependant, il y a encore les quatre fonctions restantes que nous avons utilisées dans l’élaboration du précédent l’Expert Advisor.
Je les ai simplement re-déclarés dans une nouvelle classe, d’autant plus qu’ils devaient être légèrement modifiés.
Maintenant, la méthode principale:
bool Cmagic::PositionVirtualMagic(Emagic enum_, string symbol, datetime CurrentTime)
non seulement est déclarée comme une méthode de classe Cmagic, mais dispose également d’un ensemble de paramètres différent.
Au lieu de la Magie, il obtient maintenant l’identification par le champ de la Magie, dont la position a été calculée. En outre, même si le symbole était présent dans la dernière option, il n’était utilisé que pour obtenir des informations sur l’étape du lot par le symbole. Et maintenant, il est prescrit dans le filtre et peut intervenir, sur un pied d’égalité avec les autres, dans la filtration du comptage de position.
Qu’est-ce que cela nous donne? Maintenant, nous pouvons filtrer simultanément les transactions, qui étaient ouvertes sur un instrument différent, mais par le même Expert Advisor. Ce faisant, elles ne seront pas confondues avec d’autres Expert Advisors similaires, fonctionnant sur un instrument différent. Pour être honnête, il est très difficile de décrire toutes les différentes façons d’utiliser ce nouveau système de calculs. Et le lecteur peut décider à titre personnel pour quoi il a besoin d’un système aussi compliqué. Je vous conseille vivement de ne pas compliquer les cas, où vous pouvez écrire simplement, et de ne pas craindre de telles complications lorsqu’il y a un besoin évident pour cela.
Eh bien, puisque la classe a été conçue, il est temps de la tester sur un nouvel Expert Advisor:
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- input parameters input ulong digital_name_ =4; // Digital name of Expert Advisor input ulong code_interaction_ =1; // Code of interaction input Emagic _enum =0; // Model of magic number input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.4; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- we will store the indicator's handle int MA_handle,type_MA,rezult; static ulong magic; double v[2]; datetime CurrentTime;// The variable stores the time of start of the Expert Advisor MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CProvision prov; Cmagic mg; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { magic=mg.SetMagic_request(digital_name_,code_interaction_); // Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope // used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume) CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication mg.SetMagic_result(magic);// put the information into the structure if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position { if((int)mg.cTYPE()!=type_MA)// Check if it is time to close { mg.SetMagic_result(magic);// put the information into the structure Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(), " reverse position of type ",(int)mg.cTYPE()," by ",type_MA); //cpvm.cVOLUME() - volume of virtual position rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("№",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Position by magic number has volume ",mg.cVOLUME()," open position of type",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Comme mentionné précédemment, cet Expert Advisor est extrêmement simple et a été élaboré uniquement pour démontrer les différentes capacités, exécutez-le trois fois sur un seul instrument:
Figure 6. Installation de trois Expert Advisors, avec différentes magies sur différents graphiques
Figure 7. Le résultat est le trading non conflictuel de trois Expert Advisors avec des magies différentes
Comme le montrent les impressions des messages des Expert Advisors, les trois participants ont été lancés avec succès et n’ont affiché aucun conflit.
Conclusion
En offrant la possibilité d’attribuer des ordres magiques aux opérations de trading, les créateurs de MQL5, ont grandement facilité la vie des rédacteurs Expert Advisor. Mais les développeurs ne peuvent vous fournir que des instruments - vous devez être celui qui obtient réellement les diamants.
Bonne chance et jusqu’à ce que nous nous retrouvions.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/112
- 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