MQL5 Cookbook : Utilisation d'indicateurs pour définir les conditions de trading dans les Expert Advisors
Introduction
Dans cet article, l'Expert Advisor sera enrichi d'indicateurs dont les valeurs seront utilisées pour vérifier les conditions d'ouverture des positions. Pour le pimenter, nous allons créer une liste déroulante dans les paramètres externes pour pouvoir sélectionner un des trois indicateurs de trading.
Rappelez-vous, juste au cas où : nous continuons à modifier l'Expert Advisor sur lequel nous avons travaillé tout au long des articles précédents de la série MQL5 Cookbook. La dernière version de l'Expert Advisor peut être téléchargée à partir de l'article intitulé "MQL5 Cookbook : L'histoire des transactions et la bibliothèque de fonctions pour l'obtention de propriétés de position".
De plus, cet article comportera une fonction que nous allons créer pour vérifier si les opérations de trading peuvent ou non être effectuées. La fonction d'ouverture de position sera modifiée afin de permettre à l'Expert Advisor de déterminer le mode de trading (Exécution instantanée et Exécution au marché).
Étant donné que le code de l'Expert Advisor, suite à toutes les améliorations et améliorations apportées dans les articles précédents, dépasse déjà les 1 500 lignes, il deviendra de moins en moins pratique à chaque nouvelle fonctionnalité ajoutée. Ainsi, la solution logique consiste à le diviser en plusieurs catégories en tant que fichiers de bibliothèque distincts. Maintenant que les objectifs sont fixés, commençons.
Développement de l’Expert Advisor
Nous plaçons le code source de l'Expert Advisor (*.mq5) de l'article précédent dans un dossier séparé, TestIndicatorConditions, dans lequel nous devons créer le sous-dossier Include. C'est le dossier dans lequel nous allons créer les fichiers d'inclusion (*.mqh). Ils peuvent être générés à l'aide de l'assistant MQL5 (Ctrl+N) ou créés manuellement dans le répertoire requis en tant que fichiers texte standard (*.txt) et renommés en *.mqh.
Vous trouverez ci-dessous les noms et les commentaires de tous les fichiers d'inclusion créés :
- Enums.mqh contiendra toutes les énumérations ;
- InfoPanel.mqh comportera des fonctions pour configurer le panneau d'informations, créer et supprimer des objets graphiques ;
- Errors.mqh couvrira toutes les fonctions qui renvoient des codes d'erreur et des raisons de désinitialisation ;
- TradeSignals.mqh comportera des fonctions qui remplissent des tableaux avec des prix et des valeurs d'indicateur, ainsi qu'un bloc de signal ;
- TradeFunctions.mqh contiendra des fonctions de trading ;
- ToString.mqh couvrira les fonctions qui convertissent des valeurs numériques en valeurs de chaîne de caractères ;
- Auxiliary.mqh sera utilisé pour d'autres fonctions auxiliaires.
Pour inclure ces bibliothèques dans le fichier principal, nous utilisons la directive #include. Étant donné que le fichier principal de l'Expert Advisor et le dossier du fichier d'inclusion (Include) se trouvent dans le même dossier, le code d'inclusion des fichiers sera le suivant :
//--- Include custom libraries #include "Include\Enums.mqh" #include "Include\InfoPanel.mqh" #include "Include\Errors.mqh" #include "Include\TradeSignals.mqh" #include "Include\TradeFunctions.mqh" #include "Include\ToString.mqh" #include "Include\Auxiliary.mqh"
Ensuite, nous pourrons les ouvrir et les modifier et déplacer une partie du code source du fichier principal de l'Expert Advisor.
Pour naviguer correctement dans le code, des références aux fichiers d'en-tête adjacents, ainsi qu'au fichier principal de l'Expert Advisor seront ajoutées à chaque fichier d'en-tête. Par exemple, pour notre bibliothèque de fonctions de trading, TradeFunctions.mqh, cela ressemblera à ceci :
//--- Connection with the main file of the Expert Advisor #include "..\TestIndicatorConditions.mq5" //--- Include custom libraries #include "Enums.mqh" #include "InfoPanel.mqh" #include "Errors.mqh" #include "TradeSignals.mqh" #include "ToString.mqh" #include "Auxiliary.mqh"
Pour les fichiers de même niveau d'imbrication, il suffit de spécifier simplement le nom. Pour monter d'un niveau, vous devez mettre deux points avant la barre oblique inverse dans le chemin.
Ajoutons l'énumération des indicateurs dans le fichier Enums.mqh. À des fins d'illustration, dans cet Expert Advisor, nous utiliserons deux indicateurs standard (Moving Average et Commodity Channel Index) et un indicateur personnalisé (MultiRange_PCH). Le dénombrement sera le suivant :
//--- Indicators enum ENUM_INDICATORS { MA = 0, // Moving Average CCI = 1, // CCI PCH = 2 // Price Channel };
Les paramètres externes sont modifiés comme suit :
//--- External parameters of the Expert Advisor sinput long MagicNumber=777; // Magic number sinput int Deviation=10; // Slippage input ENUM_INDICATORS Indicator=MA; // Indicator input int IndicatorPeriod=5; // Indicator period input int IndicatorSegments=2; // Number of one direction indicator segments input double Lot=0.1; // Lot input double VolumeIncrease=0.1; // Position volume increase input double VolumeIncreaseStep=10; // Step for volume increase input double StopLoss=50; // Stop Loss input double TakeProfit=100; // Take Profit input double TrailingStop=10; // Trailing Stop input bool Reverse=true; // Position reversal sinput bool ShowInfoPanel=true; // Display of the info panel
Comme mentionné ci-dessus, vous pourrez sélectionner un des trois indicateurs dans la liste déroulante du paramètre Indicateur.
Il n'y a qu'un seul paramètre applicable à tous les indicateurs pour lesquels la période d'indicateur peut être définie - IndicatorPeriod. Le paramètre NumberOfBars de la version précédente de l'Expert Advisor a été renommé en IndicatorSegments et désigne désormais le nombre de barres pendant lesquelles un indicateur donné doit monter/descendre pour satisfaire la condition d'ouverture de position.
De plus, nous avons ajouté un autre paramètre externe, VolumeIncreaseStep, qui peut être utilisé pour définir le pas d'augmentation du volume en points.
La valeur de la variable AllowedNumberOfBars (maintenant AllowedNumberOfSegments) était auparavant ajustée dans la fonction personnalisée GetBarsData(). Il sera maintenant placé dans une fonction distincte et appelé uniquement à l'initialisation.
Etant donné que la condition d'ouverture de position sera désormais vérifiée à l'aide de valeurs indicatrices, la valeur à attribuer sera toujours supérieure de deux. En d'autres termes, si la variable externe IndicatorSegments reçoit la valeur 1, la variable AllowedNumberOfSegments se verra attribuer la valeur 3, car pour satisfaire la condition (par exemple pour un BUY), la valeur de l'indicateur sur la barre complétée doit être supérieur à celui de la barre précédente. À cette fin, nous devons obtenir les trois dernières valeurs d'indicateur.
Vous trouverez ci-dessous le code de la fonction CorrectInputParameters() :
//+------------------------------------------------------------------+ //| Adjusting input parameters | //+------------------------------------------------------------------+ void CorrectInputParameters() { //--- Adjust the number of bars for the position opening condition if(AllowedNumberOfSegments<=0) { if(IndicatorSegments<=1) AllowedNumberOfSegments=3; // At least three bars are required if(IndicatorSegments>=5) AllowedNumberOfSegments=5; // but no more than 7 else AllowedNumberOfSegments=IndicatorSegments+1; // and always greater by two } }
Avant de commencer à traiter les indicateurs, créons une fonction qui vérifiera si le trading est autorisé - CheckTradingPermission(). Si le trading n'est pas autorisé pour l'une des raisons énumérées dans la fonction, la valeur zéro sera renvoyée. Cela signifie que le prochain essai devra être fait sur la barre suivante.
//+------------------------------------------------------------------+ //| Checking if trading is allowed | //+------------------------------------------------------------------+ bool CheckTradingPermission() { //--- For real-time mode if(IsRealtime()) { //--- Checking server connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return(1); //--- Permission to trade at the running program level if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED)) return(2); //--- Permission to trade at the terminal level if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return(3); //--- Permission to trade for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) return(4); //--- Permission to trade automatically for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return(5); } //--- return(0); }
Venons-en maintenant au point principal de l'article. Pour accéder aux valeurs de l'indicateur, nous devons d'abord obtenir son handle. Cela se fait à l'aide de fonctions spéciales dont les noms sont constitués du nom abrégé de l'indicateur et du symbole 'i' qui le précède.
Par exemple, l'indicateur de moyenne mobile Moving Average a la fonction correspondante iMA(). Tous les handles de l'indicateur standard dans le terminal MetaTrader 5 peuvent être obtenues en utilisant ces fonctions. La liste complète est disponible dans la section Référence MQL5 intitulée Indicateurs techniques. Si vous avez besoin d'obtenir le handle d'un indicateur personnalisé, utilisez la fonction iCustom().
Nous allons implémenter la fonction GetIndicatorHandle() où, selon l'indicateur sélectionné dans le paramètre Indicator, la valeur du handle de l'indicateur correspondant sera affectée à la variable globale indicator_handle. Le code de la fonction se trouve dans notre bibliothèque de fonctions de signal de trading (le fichier \Include\TradeSignals.mqh), tandis que la variable avec le handle de l'indicateur se trouve dans le fichier principal de l'Expert Advisor.
//+------------------------------------------------------------------+ //| Getting the indicator handle | //+------------------------------------------------------------------+ void GetIndicatorHandle() { //--- If the Moving Average indicator is selected if(Indicator==MA) indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- If the CCI indicator is selected if(Indicator==CCI) indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE); //--- If the MultiRange_PCH indicator is selected if(Indicator==PCH) indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod); //--- If the indicator handle could not be obtained if(indicator_handle==INVALID_HANDLE) Print("Failed to get the indicator handle!"); }
De plus, nous créons la fonction GetDataIndicators() où en utilisant les handles d'indicateur obtenues, nous pouvons obtenir leurs valeurs. Cela se fait à l'aide de la fonction CopyBuffer() de la même manière que pour obtenir des valeurs de barre à l'aide des fonctions CopyTime(), CopyClose(), CopyOpen(), CopyHigh() et CopyLow() considérées dans l'article intitulé "MQL5 Cookbook : Analyse des propriétés de position dans le MetaTrader 5 Strategy Tester".
Étant donné que l'indicateur peut avoir plusieurs tampons (lignes de valeurs), l'index de tampon est passé à la fonction CopyBuffer() en tant que deuxième paramètre. Les indices de tampon pour les indicateurs standard peuvent être trouvés dans la référence MQL5. Pour les indicateurs personnalisés, des indices de tampon peuvent être trouvés dans le code, à condition que le code source soit disponible. S'il n'y a pas de code, vous devrez trouver l'index à titre expérimental, en observant comment les conditions sont satisfaites dans le mode de visualisation du Strategy Tester.
Avant cela, nous devons créer des tableaux dynamiques pour les valeurs de tampon d'indicateur dans le fichier principal de l'Expert Advisor :
//--- Arrays for indicator values double indicator_buffer1[]; double indicator_buffer2[];
Le code de GetIndicatorsData() est fourni ci-dessous :
//+------------------------------------------------------------------+ //| Getting indicator values | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- If the indicator handle has been obtained if(indicator_handle!=INVALID_HANDLE) { //--- For the Moving Average or CCI indicator if(Indicator==MA || Indicator==CCI) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- For the MultiRange_PCH indicator if(Indicator==PCH) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); ArraySetAsSeries(indicator_buffer2,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- return(true); } //--- If the indicator handle has not been obtained, retry else GetIndicatorHandle(); //--- return(false); }
La fonction GetTradingSignal() a considérablement changé. Les conditions sont différentes en l'absence du poste et là où le poste existe. Pour les indicateurs Moyenne Mobile et CCI, les conditions sont les mêmes. Pour MultiRange_PCH, ils sont disposés dans un bloc séparé. Pour rendre le code plus lisible et éviter les répétitions, nous créons une fonction auxiliaire, GetSignal(), qui renvoie un signal d'ouverture ou d'inversion de position, à condition qu'une telle position existe et que l'action pertinente soit autorisée par le paramètre externe.
Voici le code de la fonction GetSignal() :
//+------------------------------------------------------------------+ //| Checking the condition and returning a signal | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- Check conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5] && indicator_buffer1[5]<indicator_buffer1[6]) return(ORDER_TYPE_SELL); //--- A Buy signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5] && indicator_buffer1[5]>indicator_buffer1[6]) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- A Buy signal if(close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
Le code de la fonction GetTradingSignal() est désormais le suivant :
//+------------------------------------------------------------------+ //| Determining trading signals | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- If there is no position if(!pos.exists) { //--- A Sell signal if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- A Buy signal if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- If the position exists if(pos.exists) { //--- Get the position type GetPositionProperties(P_TYPE); //--- Get the last deal price GetPositionProperties(P_PRICE_LAST_DEAL); //--- Block that checks conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } } //--- No signal return(WRONG_VALUE); }
Maintenant, il nous suffit de voir les modes d'exécution instantanée et d'exécution au marché qui font partie des propriétés du symbole et de modifier le code de la fonction d'ouverture de position OpenPosition() en conséquence. Les modes dont les noms sont explicites peuvent également être trouvés dans Référence MQL5 :
- Exécution Instantanée
- Execution au Marché
Veuillez noter que lorsque vous utilisez le mode d'exécution au marché, vous ne pouvez pas ouvrir une position avec les niveaux de Stop Loss et de Take Profit définis : vous devez d'abord ouvrir une position puis la modifier, en définissant les niveaux.
Ajoutons le mode d'exécution à la structure des propriétés du symbole :
//--- Symbol properties struct symbol_properties { int digits; // Number of decimal places in the price int spread; // Spread in points int stops_level; // Stops level double point; // Point value double ask; // Ask price double bid; // Bid price double volume_min; // Minimum volume for a deal double volume_max; // Maximum volume for a deal double volume_limit; // Maximum permissible volume for a position and orders in one direction double volume_step; // Minimum volume change step for a deal double offset; // Offset from the maximum possible price for a transaction double up_level; // Upper Stop level price double down_level; // Lower Stop level price ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode };
En conséquence, nous devons modifier l'énumération ENUM_SYMBOL_PROPERTIES
//--- Enumeration of position properties enum ENUM_SYMBOL_PROPERTIES { S_DIGITS = 0, S_SPREAD = 1, S_STOPSLEVEL = 2, S_POINT = 3, S_ASK = 4, S_BID = 5, S_VOLUME_MIN = 6, S_VOLUME_MAX = 7, S_VOLUME_LIMIT = 8, S_VOLUME_STEP = 9, S_FILTER = 10, S_UP_LEVEL = 11, S_DOWN_LEVEL = 12, S_EXECUTION_MODE = 13, S_ALL = 14 };
et la fonction GetSymbolProperties() :
case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //--- case S_ALL : symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits); symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits); symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits); symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits); symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits); symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //---
En conséquence, le code de la fonction OpenPosition() est désormais le suivant :
//+------------------------------------------------------------------+ //| Opening a position | //+------------------------------------------------------------------+ void OpenPosition(double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- Set the magic number in the trading structure trade.SetExpertMagicNumber(MagicNumber); //--- Set the slippage in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- The Instant Execution mode // A position can be opened with the Stop Loss and Take Profit levels set if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } //--- The Market Execution mode // First open a position and only then set the Stop Loss and Take Profit levels // *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- If there is no position, first open a position and then set Stop Loss and Take Profit if(!pos.exists) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); //--- Get the flag of presence/absence of the position pos.exists=PositionSelect(_Symbol); //--- If the position exists if(pos.exists) { //--- Set Stop Loss and Take Profit if(!trade.PositionModify(_Symbol,sl,tp)) Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged else { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } }
Il nous reste à ajouter la touche finale, très importante, aux fonctions de gestion des événements :
- OnInit
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Adjust the input parameters CorrectInputParameters(); //--- Get indicator handles GetIndicatorHandle(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); }
- OnDeinit
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) { //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); //--- Delete the indicator handle IndicatorRelease(indicator_handle); } }
- OnTick
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If the bar is not new, exit if(!CheckNewBar()) { if(IsVisualMode() || IsRealtime()) { //--- Get the properties and update the values on the panel GetPositionProperties(P_ALL); //--- Set/update the info panel SetInfoPanel(); } return; } //--- If there is a new bar else { //--- If trading is allowed if(CheckTradingPermission()==0) { if(!GetIndicatorsData()) return; GetBarsData(); // Get bar data TradingBlock(); // Check the conditions and trade ModifyTrailingStop(); // Modify the Trailing Stop level } } //--- Get the properties GetPositionProperties(P_ALL); //--- Update the info panel SetInfoPanel(); }
Maintenant que toutes les fonctions sont prêtes, nous pouvons optimiser les paramètres. Gardez à l'esprit que vous devez compiler le code à partir du fichier principal du programme.
Optimisation des paramètres et test Expert Advisor
Le testeur de stratégie doit être configuré comme indiqué ci-dessous :
Fig. 1. Paramètres du testeur de stratégie.
De plus, nous définissons les paramètres de l'Expert Advisor pour l'optimisation (voir également le fichier *.set joint avec les paramètres) :
Fig. 2. Paramètres de l'Expert Advisor.
L'optimisation a pris environ 40 minutes sur un processeur dual-core. Le tableau d'optimisation vous permet d'évaluer en partie la qualité d'un système de trading en fonction des résultats dans la zone de profit :
Fig. 3. Graphique d'optimisation.
Les résultats du test du facteur de récupération maximal sont les suivants :
Fig. 4. Résultats du test du facteur de récupération maximal.
Conclusion
L'archive téléchargeable avec les codes sources de l'Expert Advisor est jointe à l'article. Une fois extrait, vous devez placer le dossier de fichiers \TestIndicatorConditions dans <Metatrader 5 terminal>\MQL5\Experts. Pour assurer le bon fonctionnement de l'Expert Advisor, l'indicateur MultiRange_PCH doit être téléchargé et placé dans <Metatrader 5 terminal>\MQL5\Indicators.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/645
- 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