Limitations et vérifications dans Expert Advisors
Introduction
Lors de la création d’un algorithme de trading automatisé, vous devez être en mesure non seulement de traiter les prix pour créer des signaux de trading, mais aussi d’obtenir de nombreuses informations auxiliaires sur les limitations imposées au fonctionnement des Expert Advisors. Cet article vous expliquera comment :
- Obtenir des informations sur les sessions de trading ;
- Vérifiez si vous avez suffisamment d’actifs pour ouvrir une position ;
- Imposer une limitation du volume total de trading par voie d’un symbole ;
- Imposer une limitation du nombre total d’ordres ;
- Calculer la perte potentielle entre le prix d’entrée et le Stop Loss ;
- Vérifier s’il y a une nouvelle barre.
Sessions de trading et de cotation
Pour recevoir les informations sur les sessions de trading, vous devez utiliser la fonction SymbolInfoSessionTrade(), pour les sessions de cotation, utilisez la fonction SymbolInfoSessionQuote() correspondante. Les deux fonctions opèrent de la même manière : s’il existe une session avec l’indice spécifié pour le jour de la semaine spécifié (l’indexation des sessions commence à partir de zéro), alors la fonction renvoie true. L’heure de début et de fin d’une session est écrite dans les quatrième et cinquième paramètres transmis par le lien.
//--- check if there is a quotation session with the number session_index bool session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);Pour connaître toute la session du jour spécifié, appelez cette fonction dans une boucle jusqu’à ce qu’elle renvoie false.
//+------------------------------------------------------------------+ //| Display information about quotation sessions | //+------------------------------------------------------------------+ void PrintInfoForQuoteSessions(string symbol,ENUM_DAY_OF_WEEK day) { //--- start and end of session datetime start,finish; uint session_index=0; bool session_exist=true; //--- go over all sessions of this day while(session_exist) { //--- check if there is a quotation session with the number session_index session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish); //--- if there is such session if(session_exist) { //--- display the day of week, the session number and the time of start and end Print(DayToString(day),": session index=",session_index," start=", TimeToString(start,TIME_MINUTES)," finish=",TimeToString(finish-1,TIME_MINUTES|TIME_SECONDS)); } //--- increase the counter of sessions session_index++; } }
Le jour de la semaine est affiché au format chaîne à l’aide de la fonction personnalisée DayToString() qui reçoit la valeur de l’énumération ENUM_DAY_OF_WEEK comme paramètre.
//+------------------------------------------------------------------+ //| Receive the string representation of a day of week | //+------------------------------------------------------------------+ string DayToString(ENUM_DAY_OF_WEEK day) { switch(day) { case SUNDAY: return "Sunday"; case MONDAY: return "Monday"; case TUESDAY: return "Tuesday"; case WEDNESDAY: return "Wednesday"; case THURSDAY: return "Thursday"; case FRIDAY: return "Friday"; case SATURDAY: return "Saturday"; default: return "Unknown day of week"; } return ""; }
Le code final du script SymbolInfoSession.mq5 est joint au bas de l’article. Montrons ici sa partie principale seulement.
void OnStart() { //--- the array where the days of week are stored ENUM_DAY_OF_WEEK days[]={SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY}; int size=ArraySize(days); //--- Print("Quotation sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForQuoteSessions(Symbol(),days[d]); } //--- Print("Trading sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForTradeSessions(Symbol(),days[d]); } }
Vérification de la marge
Pour connaître la marge requise pour ouvrir ou augmenter une position, vous pouvez utiliser la fonction OrderCalcMargin() ; le premier paramètre qui lui est transmis est une valeur de l’énumération ENUM_ORDER_TYPE. Pour une opération d’achat, vous devez l’appeler avec le paramètre ORDER_TYPE_BUY ; pour vendre, utilisez le paramètre ORDER_TYPE_SELL . La fonction renvoie le montant de la marge en fonction du nombre de lots et du prix d’ouverture.
void OnStart() { //--- the variable to receive the value of margin double margin; //--- to receive information about the last tick MqlTick last_tick; //--- try to receive the value from the last tick if(SymbolInfoTick(Symbol(),last_tick)) { //--- reset the last error code ResetLastError(); //--- calculate margin value bool check=OrderCalcMargin(type,Symbol(),lots,last_tick.ask,margin); if(check) { PrintFormat("For the operation %s %s %.2f lot at %G required margin is %.2f %s",OrderTypeToString(type), Symbol(),lots,last_tick.ask,margin,AccountInfoString(ACCOUNT_CURRENCY)); } } else { Print("Unsuccessful execution of the SymbolInfoTick() function, error ",GetLastError()); } }
Il convient de noter que la fonction OrderCalcMargin() permet de calculer la valeur de la marge non seulement pour les ordres sur le marché, mais également pour les ordres en attente. Vous pouvez vérifier les valeurs renvoyées pour tous les types d’ordres à l’aide du script Check_Money.mq5.
La fonction OrderCalcMargin() est destinée au calcul de la taille de la marge pour les ordres en attente, car une caution monétaire peut également être requise dans certains systèmes de trading pour les ordres en attente. Habituellement, la taille de la marge pour les ordres en attente est calculée par un coefficient de la taille de la marge pour les positions longues et courtes.
Identifiant | Description | Type de propriété |
SYMBOL_MARGIN_LONG | Taux de facturation de la marge sur les positions longues | double |
SYMBOL_MARGIN_SHORT | Taux de facturation de la marge sur les positions courtes | double |
SYMBOL_MARGIN_LIMIT | Taux de facturation de la marge sur les limites d’ordres | double |
SYMBOL_MARGIN_STOP | Taux de facturation de la marge sur l’ordre Stop | double |
SYMBOL_MARGIN_STOPLIMIT | Taux de facturation de la marge sur les ordres Stop Limit | double |
Vous pouvez obtenir les valeurs de ces coefficients à l’aide du code simple :
//--- Calculate the rates of margin charging for different types of orders PrintFormat("Rate of margin charging on long positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LONG)); PrintFormat("Rate of margin charging on short positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_SHORT)); PrintFormat("Rate of margin charging on Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LIMIT)); PrintFormat("Rate of margin charging on Stop orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOP)); PrintFormat("Rate of margin charging on Stop Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOPLIMIT));
Pour les symboles Forex, les taux de marge pour les ordres en attente sont généralement égaux à 0, c’est-à-dire qu’il n’y a pas d’exigences de marge pour eux.
Résultats de l’exécution du script Check_Money.mq5.
Selon le mode de facturation de la marge, le système de gestion d’argent peut changer, ainsi que le système de trading lui-même peut connaître certaines limitations si une marge est requise pour les ordres en attente. C’est pourquoi ces paramètres peuvent également être des limitations naturelles du fonctionnement d’un Expert Advisor.
Comptabilisation des profits et pertes possibles
Lorsque vous placez un niveau d’arrêt protecteur, vous devez être prêt à le déclencher. Le risque de perte potentielle devrait être pris en compte en termes d’argent; et le OrderCalcProfit() est destiné à cet effet. Elle est très similaire à la fonction OrderCalcMargin() déjà considérée, mais elle nécessite à la fois des prix d’ouverture et de fermeture pour les calculs.
Spécifiez l’une des deux valeurs de l’énumération ENUM_ORDER_TYPE comme premier paramètre - ORDER_TYPE_BUY ou ORDER_TYPE_SELL ; d’autres types d’ordres entraîneront une erreur. Dans le dernier paramètre, vous devez passer une variable à l’aide de la référence, à laquelle la fonction OrderCalcProfit() écrira la valeur du profit / perte en cas d’exécution réussie.
Exemple d’utilisation de la fonction CalculateProfitOneLot() qui calcule le résultat lors de la clôture d’une position longue avec des niveaux spécifiés d’entrée et de sortie :
//+------------------------------------------------------------------+ //| Calculate potential profit/loss for buying 1 lot | //+------------------------------------------------------------------+ double CalculateProfitOneLot(double entry_price,double exit_price) { //--- receive the value of profit to this variable double profit=0; if(!OrderCalcProfit(ORDER_TYPE_BUY,Symbol(),1.0,entry_price,exit_price,profit)) { Print(__FUNCTION__," Failed to calculate OrderCalcProfit(). Error ",GetLastError()); } //--- return(profit); }
Le résultat du calcul de cette fonction est illustré dans la figure.
Exemple de calcul et d’affichage de la perte potentielle sur le graphique à l’aide de la fonction OrderCalcProfit().
Le code entier se trouve dans l’Expert Advisor ci-joint CalculateProfit_EA.mq5.
Vérification de l’ouverture d’une nouvelle barre
Le développement de nombreux systèmes de trading suppose que les signaux commerciaux ne sont calculés que lorsqu’une nouvelle barre apparaît ; et toutes les actions de trade ne sont effectuées qu’une seule fois. Le mode « Prix d’ouverture seulement » du testeur de stratégie dans le terminal client MetaTrader 5 est bon pour vérifier de tels systèmes de trading automatisés.
En mode « Prix d’ouverture seulement », tous les calculs des indicateurs et l’appel de la fonction OnTick() dans Expert Advisor ne sont effectués qu’une fois par barre lors des tests. C’est le mode de trading le plus rapide ; et, en règle générale, le mode le plus tolérant aux oscillations de prix insignifiantes pour la création de systèmes de trading. Dans le même temps, bien sûr, les indicateurs utilisés dans un Expert Advisor doivent être écrits correctement et ne doivent pas fausser leurs valeurs lorsqu’une nouvelle barre arrive.
Le testeur de stratégie en mode « Prix d’ouverture seulement » permet de ne pas se soucier du fait que l'Expert Advisor ne soit lancé qu'une fois par barre. Mais tout en travaillant en mode temps réel sur une démo ou sur un compte réel, un trader doit contrôler l’activité de son Expert Advisor, pour qu’il n’effectue qu’une seule opération de trade par signal reçu. Le moyen le plus simple à cette fin est de suivre l’ouverture de la barre non formée actuelle.
Pour obtenir l’heure d’ouverture de la dernière barre, vous devez utiliser la fonction SeriesInfoInteger() avec le nom spécifié du symbole, du délai et de la propriété SERIES_LASTBAR_DATE. En comparant constamment le temps d’ouverture de la barre actuelle avec celui de la barre stockée dans une variable, vous pouvez facilement détecter le moment où une nouvelle barre apparaît. Cela permet de créer la fonction personnalisée isNewBar() qui peut ressembler à ce qui suit :
//+------------------------------------------------------------------+ //| Return true if a new bar appears for the symbol/period pair | //+------------------------------------------------------------------+ bool isNewBar() { //--- remember the time of opening of the last bar in the static variable static datetime last_time=0; //--- current time datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if it is the first call of the function if(last_time==0) { //--- set time and exit last_time=lastbar_time; return(false); } //--- if the time is different if(last_time!=lastbar_time) { //--- memorize time and return true last_time=lastbar_time; return(true); } //--- if we pass to this line then the bar is not new, return false return(false); }
Un exemple d’utilisation de la fonction est donné dans l’Expert Advisor CheckLastBar.mq5 ci-joint.
Les messages de l’Expert Advisor CheckLastBar concernant l’apparition de nouvelles barres sur le délai M1.
Limitation du nombre d’ordres en attente
Si vous devez limiter le nombre d’ordres en attente actifs qui peuvent être passés simultanément sur un compte, vous pouvez écrire votre propre fonction personnalisée. Appelons cela l’OrdreIsNewautorisé() ; il vérifiera s’il est autorisé à passer un autre ordre en attente. Écrivons-le de manière à ce qu’il soit conforme aux règles du championnat de trading automatisé.
//+------------------------------------------------------------------+ //| Checks if it is allowed to place another order | //+------------------------------------------------------------------+ bool IsNewOrderAllowed() { //--- get the allowed number of pending orders on an account int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); //--- if there is no limitations, return true; you can send an order if(max_allowed_orders==0) return(true); //--- if we pass to this line, then there are limitations; detect how many orders are already active int orders=OrdersTotal(); //--- return the result of comparing return(orders<max_allowed_orders); }
La fonction est simple : obtenir le nombre autorisé d’ordres à la variable max_allowed_orders ; et si sa valeur n’est pas égale à zéro, comparer avec le nombre actuel des ordres. Cependant, cette fonction ne considère aucune autre limitation possible - la limitation du volume total autorisé de positions ouvertes et d’ordres en attente par un symbole spécifique.
Limitation du nombre de lots par un symbole spécifique
Pour obtenir la taille de la position ouverte par un symbole spécifique, vous devez tout d’abord sélectionner une position à l’aide de la fonction PositionSelect(). Et seulement après cela, vous pouvez demander le volume de la position ouverte en utilisant PositionGetDouble() ; il renvoie diverses propriétés de la position sélectionnée qui ont le double type. Écrivons la fonction PostionVolume() pour obtenir le volume de position par un symbole donné.
//+------------------------------------------------------------------+ //| Returns the size of position by a specific symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a positions by a symbol bool selected=PositionSelect(symbol); //--- the position exists if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- report about the unsuccessful attempt to select the position Print(__FUNCTION__," Failed to execute PositionSelect() for the symbol ", symbol," Error ",GetLastError()); return(-1); } }
Avant de faire une demande de trade pour placer un ordre en attente par un symbole, vous devez vérifier la limitation du volume total de position ouverte et d’ordres en attente par ce symbole - SYMBOL_VOLUME_LIMIT. S’il n’y a pas de limitation, le volume d’un ordre en attente ne peut pas dépasser le volume maximal autorisé qui peut être reçu à l’aide du volumeSymbolInfoDouble().
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
Toutefois, cette approche ne tient pas compte du volume des ordres en attente courants par le symbole spécifié. Écrivons une fonction qui calcule cette valeur :
//+------------------------------------------------------------------+ //| Returns the size of position by a specified symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a position by a symbol bool selected=PositionSelect(symbol); //--- the position exist if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- return zero if there is no position return(0); } }
En tenant compte du volume de position ouverte et du volume des ordres en attente, la vérification finale se présentera comme suit :
//+------------------------------------------------------------------+ //| Returns maximum allowed volume for an order by a symbol | //+------------------------------------------------------------------+ double NewOrderAllowedVolume(string symbol) { double allowed_volume=0; //--- get the limitation on the maximum volume of an order double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); //--- get the limitation of volume by a symbol double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); //--- get the volume of open position by a symbol double opened_volume=PositionVolume(symbol); if(opened_volume>=0) { //--- if we already used available volume if(max_volume-opened_volume<=0) return(0); //--- volume of the open position doen't exceed max_volume double orders_volume_on_symbol=PendingsVolume(symbol); allowed_volume=max_volume-opened_volume-orders_volume_on_symbol; if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume; } return(allowed_volume); }
Le code entier de l’Expert Advisor Check_Order_And_Volume_Limits.mq5 qui contient les fonctions, mentionnées dans cette section, est joint à l’article.
L’exemple de vérification à l’aide de l’expert advisor Check_Order_And_Volume_Limits sur le compte d’un participant au Championnat de trading automatisé 2010.
Vérification de l’exactitude du volume
Une partie importante de tout robot de trading est la possibilité de choisir un volume correct pour effectuer une opération de trading. Ici, nous n’allons pas parler des systèmes de gestion de l’argent et de gestion des risques, mais du volume à corriger en fonction des propriétés d’un symbole qui correspondent.
Identifiant | Description | Type de propriété |
SYMBOL_VOLUME_MIN | Volume minimal pour une transaction | double |
SYMBOL_VOLUME_MAX | Volume maximal pour une transaction | double |
SYMBOL_VOLUME_STEP | Etape de changement minimal pour l'exécution d'une transaction | double |
Pour effectuer une telle vérification, nous pouvons écrire la fonction personnalisée CheckVolumeValue() :
//+------------------------------------------------------------------+ //| Check the correctness of volume of an order | //+------------------------------------------------------------------+ bool CheckVolumeValue(double volume,string &description) { //--- Minimum allowed volume for trade operations double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); if(volume<min_volume) { description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f",min_volume); return(false); } //--- Maximum allowed volume for trade opertations double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); if(volume>max_volume) { description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f",max_volume); return(false); } //--- get the minimal volume change step double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP); int ratio=(int)MathRound(volume/volume_step); if(MathAbs(ratio*volume_step-volume)>0.0000001) { description=StringFormat("Volume is not a multiple of minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f", volume_step,ratio*volume_step); return(false); } description="Correct value of volume "; return(true); }
Vous pouvez vérifier le fonctionnement de cette fonction à l’aide du script CheckVolumeValue.mq5 joint à l’article.
Les messages du fichier CheckVolumeValue.mq5 qui vérifie que le volume est correct.
Conclusion
L’article décrit les vérifications de base pour les limitations possibles sur le fonctionnement d’un Expert Advisor, qui peuvent être confrontées lors de la création de votre propre système de trading automatisé. Ces exemples ne couvrent pas toutes les conditions possibles qui devraient être vérifiées lors du fonctionnement d’un Expert Advisor sur un compte de trade. Mais j’espère que ces exemples aideront les débutants à comprendre comment implémenter les vérifications les plus populaires dans le langage MQL5.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/22
- 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