Les principes fondamentaux de tests dans MetaTrader 5
Pourquoi avons-nous besoin d'un testeur de stratégie
L'idée du trading automatisé est attrayant par le fait que le robot de trading peut fonctionner sans interruption pendant 24 heures par jour, sept jours par semaine. Le robot ne se fatigue pas, ne doute pas ou n'a pas peur, il est totalement exempt de tout problème psychologique. Il suffit de formaliser clairement les règles de trading et de les implémenter dans les algorithmes, et le robot est prêt à travailler sans relâche. Mais avant tout, vous devez vous assurer que les deux conditions importantes suivantes sont remplies :
- L'Expert Advisor effectue les opérations de trading conformément aux règles du système de trading ;
- La stratégie de trading, mise en œuvre dans l'EA, démontre un bénéfice sur l'historique.
Pour obtenir des réponses à ces questions, nous nous tournons vers le Testeur de stratégie, inclus dans le terminal client MetaTrader 5.
Modes de génération de traits
Un Expert Advisor est un programme, écrit en MQL5, qui est exécuté à chaque fois en réponse à un événement externe. L'EA a une fonction correspondante (gestionnaire d'événements) pour chaque événement prédéfini.
L'événement NewTick (changement de prix) est l'événement principal pour l'EA et, par conséquent, nous devons générer une séquence de traits pour tester l'EA. Il existe 3 modes de génération de traits implémentés dans le Strategy Tester du terminal client MetaTrader 5 :
- Chaque trait
- OHLC 1 minute (prix OHLC avec barres minute)
- Prix d’ouverture uniquement
Le mode de base dont le plus détaillé est le mode « Chaque trait », les deux autres modes sont les simplifications du mode de base, et seront décrits en comparaison avec le mode « Chaque trait ». Considérez les trois modes afin de comprendre les différences entre eux.
« Chaque trait »
Les données de cotation historiques pour les instruments financiers sont transférées du serveur de trading au terminal client MetaTrader 5 sous la forme de barres de minutes empaquetées. Des informations détaillées sur l'occurrence des demandes et la construction des délais requis peuvent être obtenues dans le chapitre Organisation d'accès aux données du Référentiel MQL5.
L'élément minimal de l'historique des prix est la barre des minutes, à partir de laquelle vous pouvez obtenir des informations sur les quatre valeurs du prix :
- Open - le prix auquel la barre des minutes a été ouverte ;
- High - le maximum qui a été atteint au cours de cette barre minute ;
- Low - le minimum qui a été atteint au cours de cette barre minute ;
- Close - le prix de clôture de la barre.
La nouvelle barre des minutes n'est pas ouverte au moment où la nouvelle minute commence (le nombre de secondes devient égal à 0), mais lorsqu'un trait se produit - un changement de prix d'au moins un point. La figure montre la première barre des minutes de la nouvelle semaine de trading, dont l'heure d'ouverture est le 2011.01.10 00:00. L'écart de prix entre vendredi et lundi, que nous voyons sur le graphique, est courant, car les taux de change fluctuent même le week-end en réponse aux nouvelles qui tombent.
Fig. 1. L'écart de prix entre le vendredi et le lundi
S’agissant de cette barre, tout ce que nous savons est que la barre des minutes a été ouverte le 10 janvier 2011 à 00 heures 00 minutes, mais nous ne savons rien des secondes. Il aurait pu être ouvert à 00:00:12 ou 00:00:36 (12 ou 36 secondes après le début d'un nouveau jour) ou à tout autre moment dans cette minute. Mais nous savons que le prix d'ouverture de l'EURUSD était à 1,28940 à l'heure d'ouverture de la nouvelle barre des minutes.
Nous ne savons pas non plus à une seconde près quand le trait, correspondant au prix de clôture de la barre des minutes considérée, a été reçu. Nous ne savions qu'une chose - le dernier prix de clôture de la barre des minutes. Pour cette minute, le prix était de 1,28958. Le moment de l'apparition des prix High et Low est également inconnu, mais nous savons que les prix maximum et minimum se situaient aux niveaux de 1,28958 et 1,28940, respectivement.
Pour tester la stratégie de trading, nous avons besoin d'une séquence de trait, sur laquelle le travail de l'Expert Advisor sera simulé. Ainsi, pour chaque barre minute, nous connaissons les 4 points de contrôle, où le prix a définitivement été. Si une barre n'a que 4 traits, alors ces informations sont suffisantes pour effectuer un test, mais généralement le volume de traits est supérieur à 4.
Par conséquent, il est nécessaire de générer des points de contrôle supplémentaires pour les traits, qui se sont produits entre les prix Open, High, Low, et Close. Les principes du mode de génération de traits « Chaque trait » sont décrits dans l'algorithme de génération de traits dans le testeur de stratégie du terminal MetaTrader 5 dont une figure est présentée ci-dessous.
Fig. 2. Algorithme de génération de traits
Lors du test en mode « Chaque trait », la fonction OnTick() de l'EA sera appelée à chaque point de contrôle. Chaque point de contrôle est un trait d'une séquence générée. L'EA recevra l'heure et le prix du trait simulé, tout comme il le ferait en travaillant en ligne.
Important: le mode de test « Chaque trait » est le plus précis, mais en même temps, le plus long. Pour un premier test de la majorité des stratégies de trading, il suffit généralement d'utiliser l'un des deux autres modes de test.
« 1 Minute OHLC »
Le mode « Chaque trait » est le plus précis des trois modes, mais en même temps, le plus lent. L'exécution du gestionnaire OnTick() se produit à chaque trait, pendant que le volume de trait peut être assez important. Pour une stratégie dans laquelle la séquence de traits du mouvement des prix dans toute la barre n'a pas d'importance, il existe un mode de simulation plus rapide et plus approximatif - « 1 minute OHLC ».
Dans le mode «1 minute OHLC », la séquence de traits est construite uniquement par les prix OHLC des barres des minutes, le nombre de points de contrôle générés est considérablement réduit - partant le temps de test. Le lancement de la fonction OnTick() s'effectue sur tous les points de contrôle, qui sont construits par les prix des barres minutes OHLC.
Le refus de générer des traits intermédiaires supplémentaires entre les cours Open, High, Low et Close, conduit à une apparence de déterminisme rigide dans l'évolution des prix, dès lors que le prix Open est déterminé. Cela permet de créer un « Test du Graal », qui montre un joli graphique ascendant de la balance de test.
Un exemple d'un tel Graal est présenté dans la Base de code - Grr-al.
Fig. 3. Le Grr-al Expert Advisor utilise les particularités des prix OHLC
La figure montre un graphique très intéressant de ce test EA. Comment a-t-il été obtenu ? Nous connaissons 4 prix pour une barre minute, et nous savons également que le premier est le prix d'ouverture et le dernier est le prix de clôture. Nous avons les prix High et Low entre eux, et la séquence de leur apparition est inconnue, mais on sait que le prix High est supérieur ou égal au prix Open (et le prix Low est inférieur ou égal au prix Open).
Est-ce suffisant pour déterminer l'heure de réception du prix d'ouverture, puis analyser le prochain trait, pour déterminer si le prix High ou Low vient avant lui ? Si le prix est inférieur au prix d'ouverture, alors nous avons un prix Low et achetons à ce trait, le prochain trait correspondra au prix High, au niveau duquel nous clôturerons l'achat pour lancer la vente. Le trait suivant est le dernier, c'est le prix de clôture, et nous clôturons la vente avec cela.
Si après le prix, nous recevons un trait avec un prix supérieur au prix d'ouverture, alors la séquence des transactions est inversée. Effectuez une barre des minutes dans ce mode « cheat », et attendez la suivante.
Lors du test d'un tel EA sur l'historique, tout se passe bien, mais une fois que nous l'avons lancé en ligne, la vérité commence à se révéler - la ligne d'équilibre reste stable, mais se dirige vers le bas. Pour exposer ce trait, nous devons simplement exécuter l'EA en mode « Chaque trait ».
Remarque : Si les résultats des tests de l'EA dans les modes de test approximatifs (« 1 minute OHLC » et « Open Prices only ») semblent trop bons, assurez-vous de les tester dans le mode « Chaque trait ».
« Prix d’ouverture uniquement »
Dans ce mode, les traits sont générés en fonction des prix OHLC du délai sélectionné pour le test. La fonction OnTick() de l'Expert Advisor ne s'exécute qu'au début de la barre au prix Open. En raison de cette fonctionnalité, les niveaux d'arrêt et en attente peuvent se déclencher à un prix différent de celui spécifié (en particulier lors de tests sur des délais plus longs). Par contre, nous avons la possibilité d'exécuter rapidement un test d'évaluation de l'Expert Advisor.
Une exception dans la génération de traits en mode « Prix d’ouverture uniquement » est les périodes W1 et MN1 : pour ces délais, les traits sont générés pour les prix OHLC de chaque jour, et non les prix OHLC de la semaine ou du mois.
- Le calcul des exigences de marge ;
- Le déclenchement des niveaux des Stop Loss et Take Profit ;
- Le déclenchement des ordres en cours ;
- La suppression des ordres en attente expirés.
S'il n'y a pas de positions ouvertes ou d'ordres en attente, nous n'avons pas besoin d'effectuer ces contrôles sur les traits cachés, et l'augmentation de la vitesse peut être assez substantielle. Ce mode « Prix d'ouverture uniquement » est bien adapté pour tester les stratégies, qui ne traitent les transactions qu'à l'ouverture de la barre et n'utilisent pas les ordres en attente, ainsi que les ordres StopLoss et TakeProfit. Pour la classe de telles stratégies, la précision nécessaire des tests est préservée.
Utilisons le Moving Average Expert Advisor du paquet standard comme exemple d'EA, qui peut être testé dans n'importe quel mode. La logique de cet EA est élaborée de telle manière que toutes les décisions sont prises à l'ouverture de la barre, et les transactions sont réalisées immédiatement, sans utilisation d'ordres en attente.
Exécutez un test de l'EA sur EURUSD H1 dans un intervalle de 2010.01.09 à 2010.31.12, et comparez les graphiques. La figure montre le graphique d'équilibre du rapport de test pour les trois modes.
Fig. 4. Le graphique de test de Moving Average.mq5 EA du paquet standard ne dépend pas du mode de test (cliquez sur l'image pour zoomer)
Comme vous pouvez le voir, les graphiques de différents modes de test sont exactement les mêmes pour la moyenne mobile EA du paquet standard.
Il y a quelques limitations sur le mode « Prix d’ouverture uniquement » :
- Vous ne pouvez pas utiliser le mode d’exécution Délai aléatoire.
- Dans l'Expert Advisor testé, vous ne pouvez pas accéder aux données du délai inférieur à celui utilisé pour test/optimisation. Par exemple, si vous effectuez des tests/optimisations sur la période H1, vous pouvez accéder aux données de H2, H3, H4, etc., mais pas M30, M20, M10, etc. De plus, les délais plus élevés auxquels on accède doivent être des multiples du délai de test. Par exemple, si vous exécutez des tests dans M20, vous ne pouvez pas accéder aux données de M30, mais il est possible d'accéder à H1. Ces limitations sont liées à l'impossibilité d'obtenir des données de délais inférieurs ou non multiples à partir des barres générées lors des tests/optimisations.
- Les limitations d'accès aux données d'autres délais s'appliquent également à d'autres symboles dont les données sont utilisées par l'Expert Advisor. Dans ce cas, la limitation pour chaque symbole dépend du premier intervalle de temps auquel on accède pendant le test/l'optimisation. Supposons qu'au cours des tests sur EURUSD H1, un Expert Advisor accède aux données de GBPUSD M20. Dans ce cas, l'Expert Advisor pourra continuer à utiliser les données EURUSD H1, H2, etc., ainsi que GBPUSD M20, H1, H2, etc.
Remarque : Le mode « Prix d’ouverture uniquement » a le temps de test le plus rapide, mais il ne convient pas à toutes les stratégies de trading. Sélectionnez le mode de test souhaité en fonction des caractéristiques du système de trading.
Pour conclure la section sur les modes de génération de traits, considérons une comparaison visuelle des différents modes de génération de traits pour l'EURUSD, pour deux barres M15 sur un intervalle de 2011.01.11 21:00:00 - 2011.01.11 21:30:00.
Les traits ont été enregistrés dans différents fichiers à l'aide de WriteTicksFromTester.mq5 EA et la fin de ces noms de fichiers est spécifiée dans les paramètres d'entrée filenameEveryTick, filenameOHLC et filenameOpenPrice.
Fig. 5. On peut spécifier les dates de début et de fin des traits (les variables de début et de fin) pour l'Expert Advisor WriteTicksFromTester
Pour obtenir trois fichiers avec trois séquences de traits (pour chacun des modes suivants « Chaque trait », « 1 minute OHLC » et « Prix d’ouverture uniquement »), l'EA a été lancé trois fois dans les modes correspondants, en séries. Ensuite, les données de ces trois fichiers ont été affichées sur le graphique à l'aide de l'indicateur TicksFromTester.mq5. Le code indicateur est joint à cet article.
Fig. 6. La séquence de traits dans le Testeur de stratégie du terminal MetaTrader 5 dans trois modes de test différents
Par défaut, toutes les opérations de fichier dans le langage MQL5 sont effectuées dans le « sandbox de fichier », et pendant les tests, l'EA n'a accès qu'à son propre « sandbox de fichier ». Pour que l'indicateur et l'EA fonctionnent avec les fichiers d'un dossier pendant les tests, nous avons utilisé le drapeau FILE_COMMON. Un exemple de code de l'EA :
//--- open the file file=FileOpen(filename,FILE_WRITE|FILE_CSV|FILE_COMMON,";"); //--- check file handle if(file==INVALID_HANDLE) { PrintFormat("Error in opening of file %s for writing. Error code=%d",filename,GetLastError()); return; } else { PrintFormat("The file will be created in %s folder",TerminalInfoString(TERMINAL_COMMONDATA_PATH)); }
Pour lire les données dans l'indicateur, nous avons également utilisé le drapeau FILE_COMMON. Cela nous a permis d'éviter de transférer manuellement les fichiers nécessaires d'un dossier à un autre.
//--- open the file int file=FileOpen(fname,FILE_READ|FILE_CSV|FILE_COMMON,";"); //--- check file handle if(file==INVALID_HANDLE) { PrintFormat("Error in open of file %s for reading. Error code=%d",fname,GetLastError()); return; } else { PrintFormat("File will be opened from %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH)); }
Simulation spread
La différence de prix entre les prix Bid et Ask s'appelle le spread. Lors des tests, la propagation n'est pas modélisée mais est extraite des données historiques. Si l'écart est inférieur ou égal à zéro dans les données historiques, alors l'écart pour la durée des données historiques demandées est utilisé par l'agent de test.
Dans le Testeur de stratégie, le spread est toujours considéré comme flottant. C'est-à-dire que le SymbolInfoInteger(symbole, SYMBOL_SPREAD_FLOAT a toujours un renvoi vrai.
De plus, les données historiques contiennent des cotations et des volumes de transactions. Pour le stockage et la récupération des données, nous utilisons une structure spéciale MqlRates :
struct MqlRates { datetime time; // opening bar time double open; // opening price Open double high; // the highest price High double low; // the lowest price Low double close; // the closing price Close long tick_volume; // the tick volume int spread; // spread long real_volume; // market volume };
Les variables globales du terminal client
Lors des tests, les variables globales du terminal client sont également émulées, mais elles ne sont pas liées aux variables globales du terminal, qui peuvent être vues dans le terminal à l'aide de la touche F3. Cela signifie que toutes les opérations avec les variables globales du terminal, lors du test, ont lieu en dehors du terminal client (dans l'agent de test).
Le calcul des indicateurs pendant les tests
En mode temps réel, les valeurs de l'indicateur sont calculées à chaque trait. Le Strategy Tester a adopté un modèle rentable pour le calcul des indicateurs - les indicateurs ne sont recalculés qu'immédiatement avant l'exécution de l'EA. Cela signifie que le recalcul des indicateurs se fait avant l'appel des fonctions OnTick(), OnTrade() et OnTimer().
Peu importe qu'il y ait ou non un appel pour l'indicateur dans un gestionnaire d'événements spécifique, tous les indicateurs dont les descripteurs ont été créés par les fonctions iCustom() ou IndicatorCreate() seront recalculés avant d'appeler le gestionnaire d'événements.
Par conséquent, lors d'un test en mode « Chaque trait », le calcul des indicateurs a lieu avant l'appel de la fonction OnTick().
Si le timer est activé dans l'EA, en utilisant la fonction EventSetTimer(), alors les indicateurs seront recalculés avant chaque appel du gestionnaire OnTimer(). Par conséquent, le temps de test peut être considérablement augmenté avec l'utilisation d'indicateurs, écrits de manière non optimale.
Chargement de l'historique pendant les tests
L'historique d'un symbole testé est synchronisé et chargé par le terminal à partir du serveur commercial avant de démarrer le processus de test. Lors de la première fois, le terminal charge tout l'historique disponible d'un symbole afin de ne pas le redemander ultérieurement. De plus, seules les nouvelles données sont chargées.
Un agent de test reçoit l'historique d'un symbole testé du terminal client juste après le début du test. Si des données d'autres instruments sont utilisées dans le processus de test (par exemple, il s'agit d'un Expert Advisor multidevises), l'agent de test demande l'historique requis au terminal client lors du premier appel à ces données. Si des données historiques sont disponibles dans le terminal, elles sont immédiatement transmises à l'agent de test. Si les données ne sont pas disponibles, le terminal les demande et les télécharge depuis le serveur, puis les transmet à l'agent de test.
Les données d'instruments supplémentaires sont également requises pour le calcul des taux croisés pour les opérations de trading. Par exemple, lors du test d'une stratégie sur EURCHF avec la devise de dépôt en USD, avant de traiter la première opération de trading, l'agent de test demande les données historique d'EURUSD et USDCHF au terminal client, bien que la stratégie ne contienne pas d'appel d'utilisation directe de ces symboles.
Avant de tester une stratégie multi-devises, il est recommandé de télécharger toutes les données historiques nécessaires sur le terminal client. Cela aidera à éviter les retards dans le test/l'optimisation associés au téléchargement des données requises. Vous pouvez télécharger l'historique, par exemple, en ouvrant les graphiques appropriés et en les faisant défiler jusqu'au début de l'historique. Un exemple de chargement forcé de l'historique dans le terminal est disponible dans la section Organisation d’accès aux données du Référentiel MQL5.
Test multi-devises
Le Testeur de stratégie nous permet d'effectuer un test de stratégies, en tradant sur plusieurs symboles. De tels EA sont traditionnellement appelés Expert Advisors multidevises, car à l'origine, dans les plates-formes précédentes, les tests n'étaient effectués que pour un seul symbole. Dans le Testeur de stratégie du terminal MetaTrader 5, nous pouvons modéliser le trading pour tous les symboles disponibles.
Le testeur charge l'historique des symboles utilisés depuis le terminal client (pas depuis le serveur de trading !) automatiquement lors du premier appel des données de symboles.
L'agent de test ne télécharge que l'historique manquant, avec une petite marge pour fournir les données nécessaires sur l'historique, pour le calcul des indicateurs au début du test. Pour les délais D1 et moins, le volume minimum de l'historique téléchargé est d'un an.
Ainsi, si l'on fait un test sur un intervalle 2010.11.01-2010.12.01 (test sur un intervalle d'un mois) avec une période de M15 (chaque barre est égale à 15 minutes), alors le terminal se verra demander l'historique de l'instrument pour toute l'année 2010. Pour la délai hebdomadaire, nous demanderons un historique de 100 barres, soit environ deux ans (une année compte 52 semaines). Pour les tests sur un délai mensuel, l'agent demandera l'historique de 8 ans (12 mois x 8 ans = 96 mois).
S'il n'y a pas de barres nécessaires, la date de début du test sera automatiquement décalée du passé au présent pour fournir la réserve de barres nécessaire avant le test.
Pendant les tests, le « Market Watch » est également émulé, à partir duquel on peut obtenir des informations sur les symboles.
Par défaut, au début du test, il n'y a qu'un seul symbole dans le « Market Watch » du Testeur de stratégie - le symbole sur lequel le test s'exécute. Tous les symboles nécessaires sont automatiquement connectés à la «Market Watch » du testeur de stratégie (pas de terminal !) lorsqu'on s’y réfère.
Avant de commencer le test d'un Expert Advisor multidevises, il est nécessaire de sélectionner les symboles requis pour le test dans le « Market Watch » du terminal et de charger les données requises. Lors du premier appel d'un symbole « foreign », son historique sera automatiquement synchronisé entre l'agent testeur et le terminal client. Un symbole « foreign » est le symbole autre que celui sur lequel le test est en cours d'exécution.
La référence aux données d'un « autre » symbole se produit dans les cas suivants :
- Lorsque vous utilisez la fonction d'indicateurs techniques et IndicatorCreate() sur le symbole/délai ;
- La demande aux données « Market Watch » pour l'autre symbole :
- Demande de la série temporelle d'un symbole/délai au moyen des fonctions suivantes :
Au moment du premier appel à un « autre » symbole, le processus de test est arrêté et l'historique est téléchargé pour le symbole/délai, du terminal vers l'agent de test. En même temps, la génération d'une séquence de traits pour ce symbole est effectuée.
Une séquence de trait individuel est générée pour chaque symbole, selon le mode de génération de traits sélectionné. Vous pouvez également demander explicitement l'historique des symboles souhaités en appelant le SymbolSelect() dans le gestionnaire OnInit() - le téléchargement de l'historique sera effectué juste avant le test de l'Expert Advisor.
Ainsi, il ne nécessite aucun effort supplémentaire pour effectuer des tests multi-devises dans le terminal client MetaTrader 5. Ouvrez simplement les graphiques des symboles appropriés dans le terminal client. L'historique sera automatiquement téléchargé depuis le serveur de trading pour tous les symboles requis, à condition qu'il contienne ces données.
Simulation du temps dans le testeur de stratégie
Lors des tests, l'heure locale TimeLocal() est toujours égale à l'heure du serveur TimeTradeServer(). À son tour, l'heure du serveur est toujours égale à l'heure correspondant à l'heure GMT - TimeGMT(). De cette façon, toutes ces fonctions s'affichent en même temps pendant le test.
L'absence de différence entre le GMT, le Local et le temps du serveur dans le Testeur de stratégie s’effectue délibérément dans le cas où il n'y a pas de connexion au serveur. Les résultats du test doivent toujours être les mêmes, qu'il y ait ou non une connexion. Les informations sur l'heure du serveur ne sont pas stockées localement et proviennent du serveur.
La fonction OnTimer () dans le testeur de stratégie
MQL5 offre la possibilité de gérer les événements de minuterie. L'appel du gestionnaire OnTimer() s’exécute quel que soit le mode de test.
Cela signifie que si un test est exécuté en mode « Prix d’ouverture uniquement » pour la période H4 et que l'EA dispose d’une minuterie réglée sur un appel par seconde, alors à l'ouverture de chaque barre H4, le gestionnaire OnTick() sera appelé une fois, et le gestionnaire OnTimer() sera appelé 14 400 fois (3 600 secondes * 4 heures). Le montant par lequel le temps de test de l'EA sera augmenté est fonction de la logique de l'EA.
Pour vérifier la dépendance du temps de test par rapport à la fréquence donnée du timer, un simple EA, sans aucune opération de trading, a été écrit.
//--- input parameters input int timer=1; // timer value, sec input bool timer_switch_on=true; // timer on //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- run the timer if timer_switch_on==true if(timer_switch_on) { EventSetTimer(timer); } //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- stop the timer EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- // take no actions, the body of the handler is empty } //+------------------------------------------------------------------+
Les mesures du temps de test ont été prises à différentes valeurs du paramètre timer (périodicité de l'événement Timer). Sur les données obtenues, nous traçons un temps de test en fonction de la période du Timer.
Fig. 7. Temps de test en fonction de la période de la minuterie
On peut clairement voir que plus le paramètre timer est petit, lors de l'initialisation de la fonction EventSetTimer(Timer), plus petite est la période (Period) entre les appels du gestionnaire OnTimer(), et plus grand est le temps de test T, dans les mêmes autres conditions.
La fonction Sleep () dans le testeur de stratégie
La fonction Sleep() permet à l'EA ou au script de suspendre l'exécution du programme mql5 pendant un certain temps, lorsqu'il travaille sur le graphique. Cela peut être utile lorsque vous demandez des données qui ne sont pas prêtes au moment de la demande et vous devez attendre qu'elles soient prêtes. Un exemple détaillé d'utilisation de la fonction Sleep() peut être trouvé dans la section Provision d’accès aux données.
Le processus de test n'est pas ralenti par les appels Sleep(). Lorsque vous appelez Sleep(), les traits générés sont « exécutés » dans un délai spécifié, ce qui peut entraîner le déclenchement d'ordres en attente, d'arrêts, etc. Après un appel Sleep(), le temps simulé dans le Testeur de stratégie augmente d'un intervalle, spécifié dans le paramètre de la fonction Sleep.
Si, à la suite de l'exécution de la fonction Sleep(), l'heure actuelle dans le Testeur de stratégie a dépassé la période de test, vous recevrez une erreur « Boucle Infinite Sleep détectée lors du test ». Si vous recevez cette erreur, les résultats des tests ne sont pas rejetés, tous les calculs sont effectués dans leur intégralité (nombre de transactions, la profondeur, etc.), et les résultats de ces tests sont transmis au terminal.
La fonction Sleep() ne fonctionnera pas dans OnDeinit(), car après son appel, le temps de test sera garanti pour dépasser la plage de l'intervalle de test.
Fig. 8. Le schéma d'utilisation de la fonction Sleep() dans le Testeur de stratégie du terminal MetaTrader 5
Utilisation du testeur de stratégie pour les problèmes d'optimisation dans les calculs mathématiques
Le testeur du terminal MetaTrader 5 peut être utilisé non seulement pour tester des stratégies de trading, mais également pour des calculs mathématiques. Pour l'utiliser, il faut sélectionner le mode « Calculs mathématiques » :
Dans ce cas, seules trois fonctions seront appelées : OnInit(), OnTester(), OnDeinit(). En mode « Calculs mathématiques », le Testeur de stratégie ne génère aucun trait et télécharge l'historique.
Le testeur de stratégie fonctionne également en mode « Calculs mathématiques » si vous spécifiez une date de début supérieure à la date de clôture.
Lors de l'utilisation du testeur pour résoudre des problèmes mathématiques, le téléchargement de l'historique et la génération de traits ne se produisent pas.
Un problème mathématique typique à résoudre dans le testeur de stratégie MetaTrader 5 - la recherche d'un extremum d'une fonction avec de nombreuses variables.
Pour le résoudre, nous avons besoin de ce qui suit :
- Le calcul de la valeur de la fonction doit être situé dans la fonction OnTester() ;
- Les paramètres de la fonction doivent être définis comme variables d’entrée de l'Expert Advisor ;
Compilez l'EA, ouvrez la fenêtre « Testeur de stratégie ». Dans l'onglet « Paramètres d'entrée », sélectionnez les variables d'entrée requises et définissez l'ensemble des valeurs des paramètres en spécifiant les valeurs de démarrage, d'arrêt et de graduation pour chacune des variables de fonction.
Sélectionnez le type d'optimisation - « Algorithme complet lent » (recherche complète de l'espace des paramètres) ou « Algorithme génétique rapide ». Pour une recherche simple de l'extremum de la fonction, il vaut mieux choisir une optimisation rapide, mais si vous voulez calculer les valeurs de l'ensemble des variables, il est préférable d'utiliser l'optimisation lente.
Sélectionnez le mode « Calcul mathématique » et à l'aide du bouton « Démarrer », lancez la procédure d'optimisation. Notez que lors de l'optimisation, le Testeur de stratégie recherchera les valeurs maximales de la fonction. Pour trouver un minimum local, retournez l'inverse de la valeur de la fonction calculée à partir de la fonction OnTester :
return(1/function_value);
Il faut vérifier que la function_value n'est pas égale à zéro, car sinon on peut obtenir une erreur critique de division par zéro.
Il existe un autre moyen qui est plus pratique, qui ne fausse pas les résultats de l'optimisation, et qui a été suggéré par les lecteurs de cet article :
return(-function_value);
Cette option ne nécessite pas de vérifier que function_value_ est égal à zéro, et la surface des résultats d'optimisation dans une représentation 3D a la même forme, mais est symétrique de l'original.
À titre d'exemple, nous fournissons la fonction sink() :
Le code de l'EA pour trouver l'extremum de cette fonction est placé dans le OnTester() :
//+------------------------------------------------------------------+ //| Sink.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //--- input parameters input double x=-3.0; // start=-3, step=0.05, stop=3 input double y=-3.0; // start=-3, step=0.05, stop=3 //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- double sink=MathSin(x*x+y*y); //--- return(sink); } //+------------------------------------------------------------------+Effectuez une optimisation et consultez les résultats d’ optimisation sous la forme d'un graphique 2D.
Fig. 9. Les résultats de l'optimisation complète de la fonction sink (x*x+y*y) sous forme de graphique 2D
Plus la valeur est bonne pour une paire donnée de paramètres (x, y), plus la couleur est saturée. Comme on pouvait s'y attendre du point de vue de la forme de la formule sink(), ses valeurs forment des cercles concentriques avec un centre (0,0). On peut voir dans le graphique 3D, que la fonction sink() n'a pas d'extremum global unique :
La Synchronisation des Barres en mode « Prix d’ouverture uniquement »
Le testeur dans le terminal client MetaTrader 5 nous permet de vérifier les EA dits « multi-devises ». Un EA multi-devises - est un EA qui se trade sur deux symboles ou plus.
Le test de stratégies, qui se tradent sur plusieurs symboles, impose quelques exigences techniques supplémentaires au testeur :
- La génération de traits pour ces symboles ;
- Le calcul des valeurs d’indicateur pour ces symboles ;
- Le calcul des exigences de marge pour ces symboles ;
- Synchronisation des séquences de traits générées pour tous les symboles de trading.
Le Testeur de stratégie génère et exécute une séquence de traits pour chaque instrument en fonction du mode de trading sélectionné. En même temps, une nouvelle barre pour chaque symbole est ouverte, quelle que soit la façon dont la barre s'est ouverte sur un autre symbole. Cela signifie que lors du test d'un EA multi-devises, une situation peut se produire (et c'est souvent le cas), lorsque pour un instrument une nouvelle barre s'est déjà ouverte, et pour l'autre non. Ainsi, dans les tests, tout se passe comme dans la réalité.
Cette simulation authentique de l'historique dans le testeur ne pose aucun problème tant que les modes de test « Chaque trait » et « 1 minute OHLC » sont utilisés. Pour ces modes, suffisamment de traits sont générés pour un chandelier, pour pouvoir attendre que la synchronisation des barres de différents symboles ait lieu. Mais comment tester les stratégies multi-devises en mode « Prix d’ouverture uniquement », si la synchronisation des barres sur les instruments de trading est obligatoire ? Dans ce mode, l'EA n'est appelé que sur un seul trait, qui correspond à l'heure d'ouverture des barres.
Nous allons l'illustrer avec un exemple : si nous testons un EA sur l'EURUSD et qu'un nouveau chandelier horaire s'est ouvert sur l'EURUSD, alors nous pouvons facilement reconnaître ce fait - en testant en mode « Prix d'ouverture uniquement », l'événement NewTick correspond au moment de l'ouverture de la barre sur la période d'essai. Mais il n'y a aucune garantie qui indique que le nouveau chandelier s'est ouvert sur le symbole USDJPY, qui est utilisé dans l'EA.
Dans des circonstances normales, il suffit de terminer le travail de la fonction OnTick() et de vérifier l'émergence d'une nouvelle barre sur l'USDJPY au prochain trait. Mais lors des tests en mode « Prix d’ouverture uniquement », il n'y aura pas d'autre trait, et il peut donc sembler que ce mode ne convient pas pour tester les EA multi-devises. Mais ce n'est pas le cas - n'oubliez pas que le testeur de MetaTrader 5 se comporte comme cela se ferait dans la vraie vie. Vous pouvez attendre qu'une nouvelle barre soit ouverte sur un autre symbole au moyen de la fonction Sleep() !
Le code de l'EA Synchronize_Bars_Use_Sleep.mq5, montre un exemple de synchronisation de barres en mode « Prix d’ouverture uniquement » :
//+------------------------------------------------------------------+ //| Synchronize_Bars_Use_Sleep.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //--- input parameters input string other_symbol="USDJPY"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- check symbol if(_Symbol==other_symbol) { PrintFormat("You have to specify the other symbol in input parameters or select other symbol in Strategy Tester!"); //--- forced stop testing return(-1); } //--- return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- static variable, used for storage of last bar time static datetime last_bar_time=0; //--- sync flag static bool synchonized=false; //--- if static variable isn't initialized if(last_bar_time==0) { //--- it's first call, save bar time and exit last_bar_time=(datetime)SeriesInfoInteger(_Symbol,Period(),SERIES_LASTBAR_DATE); PrintFormat("The last_bar_time variable is initialized with value %s",TimeToString(last_bar_time)); } //--- get open time of the last bar of chart symbol datetime curr_time=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if times aren't equal if(curr_time!=last_bar_time) { //--- save open bar time to the static variable last_bar_time=curr_time; //--- not synchronized synchonized=false; //--- print message PrintFormat("A new bar has appeared on symbol %s at %s",_Symbol,TimeToString(TimeCurrent())); } //--- open time of the other symbol's bar datetime other_time; //--- loop until the open time of other symbol become equal to curr_time while(!(curr_time==(other_time=(datetime)SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)) && !synchonized)) { PrintFormat("Waiting 5 seconds.."); //--- wait 5 seconds and call SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE) Sleep(5000); } //--- bars are synchronized synchonized=true; PrintFormat("Open bar time of the chart symbol %s: is %s",_Symbol,TimeToString(last_bar_time)); PrintFormat("Open bar time of the symbol %s: is %s",other_symbol,TimeToString(other_time)); //--- TimeCurrent() is not useful, use TimeTradeServer() Print("The bars are synchronized at ",TimeToString(TimeTradeServer(),TIME_SECONDS)); } //+------------------------------------------------------------------+
Remarquez la dernière ligne de l'EA, qui affiche l'heure actuelle à laquelle le fait de synchronisation a été établi :
Print("The bars synchronized at ",TimeToString(TimeTradeServer(),TIME_SECONDS));
Pour afficher l'heure actuelle, nous avons utilisé la fonction TimeTradeServer () plutôt que TimeCurrent (). La fonction TimeCurrent() renvoie l'heure du dernier trait, qui ne change pas après l'utilisation de Sleep(). Coulez l'EA en mode « Prix d’ouverture uniquement», et vous verrez un message sur la synchronisation des barres.
Utilisez la fonction TimeTradeServer() au lieu de TimeCurrent(), si vous avez besoin d'obtenir l'heure actuelle du serveur, et non l'heure du dernier trait d'arrivée.
Il existe une autre façon de synchroniser les barres - en utilisant une minuterie. Un exemple d'un tel EA est Synchronize_Bars_Use_OnTimer.mq5, qui est joint à cet article.
La fonction IndicatorRelease() dans le testeur
Une fois terminé un seul test, un graphique de l'instrument est automatiquement ouvert, qui affiche les transactions terminées et les indicateurs utilisés dans l'EA. Cela permet de vérifier visuellement les points d'entrée et de sortie, et de les comparer avec les valeurs des indicateurs.
Remarque : les indicateurs, affichés sur le graphique, qui s'ouvre automatiquement après la fin des tests, sont recalculés une fois les tests terminés, même si ces indicateurs ont été utilisés dans l'EA testé.
Mais dans certains cas, le programmeur peut vouloir masquer les informations sur les indicateurs impliqués dans les algorithmes de trading. Par exemple, le code de l'EA est loué ou vendu sous forme de fichier exécutable, sans la fourniture du code source. A cet effet, la fonction IndicatorRelease() convient.
Si le terminal définit un modèle avec le nom tester.tpl dans le répertoire/profiles/modèles du terminal client, alors il sera appliqué au graphique ouvert. En son absence, le modèle par défaut est appliqué. (default.tpl).
La fonction IndicatorRelease() est à l'origine destinée à libérer la partie calcul de l'indicateur, si elle n'est plus nécessaire. Cela vous permet d'économiser à la fois la mémoire et les ressources CPU, car chaque trait demande un calcul d'indicateur. Son deuxième objectif - est d'interdire l'affichage d'un indicateur sur le graphique de test, après une seule série d'essais.
Pour interdire l'affichage de l'indicateur sur le graphique après le test, appelez l’IndicatorRelease() avec le descripteur de l’indicateur dans le gestionnaire OnDeinit(). La fonction OnDeinit() est toujours appelée après l'achèvement du graphique de test et avant son affichage.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- bool hidden=IndicatorRelease(handle_ind); if(hidden) Print("IndicatorRelease() successfully completed"); else Print("IndicatorRelease() returned false. Error code ",GetLastError()); }
Gestion d’événements dans le testeur
La présence du gestionnaire OnTick() dans l'EA n'est pas obligatoire pour qu'il soit soumis à des tests sur les données historiques dans le testeur MetaTrader 5. Il suffit que l'EA ti contienne au moins l'un des gestionnaires de fonctions suivants :
- OnTick() - Gestionnaire d'événements pour un nouveau trait ;
- OnTrade() - Gestionnaire d'événements de trading ;
- OnTimer() - Gestionnaire d'événements d'une arrivée de signal de la minuterie ;
- OnChartEvent () - un gestionnaire pour les événements client.
Lors des tests dans un EA, nous pouvons gérer des événements personnalisés à l'aide de la fonction OnChartEvent(), mais dans les indicateurs, cette fonction ne peut pas être appelée dans le testeur. Même si l'indicateur a le gestionnaire d'événements OnChartEvent() et que cet indicateur est utilisé dans l'EA testé, l'indicateur lui-même ne recevra aucun événement personnalisé.
Pendant les tests, un indicateur peut générer des événements personnalisés à l'aide de la fonction EventChartCustom() et l'EA peut traiter cet événement dans OnChartEvent().
Agents de test
Les tests dans le terminal client MetaTrader 5 sont effectués à l'aide de des agents de test. Les agents locaux sont créés et activés automatiquement. Le nombre par défaut d'objets locaux correspond au nombre de cœurs d'un ordinateur.
Chaque agent de test dispose de sa propre copie de la variables globales, qui n'est pas liée au terminal client. Le terminal lui-même est le répartiteur, qui distribue les tâches aux agents locaux et distants. Après avoir exécuté une tâche sur le test d'un EA, avec les paramètres donnés, l'agent renvoie les résultats au terminal. Avec un seul test, un seul agent est utilisé.
L'agent stocke l'historique, reçu du terminal, dans des dossiers séparés, au moyen du nom de l'instrument, de sorte que l'historique pour EURUSD est stocké dans un dossier nommé EURUSD. De plus, l'historique des instruments est séparé par leurs sources. La structure de stockage de l'historique se présente comme suit :
Par exemple, l'historique de l'EURUSD du serveur MetaQuotes-Demo peut être stocké dans le dossier tester_catalog\Agent-127.0.0.1-3000\bases\MetaQuotes-Demo\EURUSD.
Un agent local, une fois les tests terminés, passe en mode veille, attendant la tâche suivante pendant 5 minutes supplémentaires, afin de ne pas perdre de temps au lancement du prochain appel. Ce n'est qu'une fois la période d'attente terminée que l'agent local s'arrête et procède au déchargement de la mémoire CPU.
En cas de fin anticipée du test, côté utilisateur (le bouton « Annuler »), ainsi qu'à la fermeture du terminal client, tous les agents locaux arrêtent immédiatement leur travail et sont déchargés de la mémoire.
L'échange de données entre le Terminal et l'Agent
Lorsque vous exécutez un test, le terminal client se prépare à envoyer à l'agent un certain nombre de blocs de paramètres : - Paramètres d'entrée pour le test (mode de simulation, intervalle de test, instruments, critère d'optimisation, etc.)
- La liste des symboles sélectionnés dans le « Market Watch »
- La spécification du symbole de test (la taille du contrat, les marges admissibles du marché pour définir un StopLoss et un Takeprofit, etc.)
- L'Expert Advisor testé et les valeurs de ses informations de paramètres
- d’entrée sur les fichiers supplémentaires (bibliothèques, indicateurs, fichiers de données - # property tester_ ...)
tester_indicator
Nom d'un indicateur personnalisé sous format « indicator_name.ex5 ». Les indicateurs nécessitant d'être testés sont définis automatiquement à partir de l'appel de la fonction iCustom(), si le paramètre correspondant est défini par une chaîne de caractères constante. Pour tous les autres cas (utilisation de la fonction IndicatorCreate() ou utilisation d'une chaîne de caractères non constante dans le paramètre définissant le nom de l'indicateur), cette propriété est nécessaire
tester_file
Le nom du fichier d'un testeur avec son extension, entre guillemets (comme une chaîne constante). Le fichier spécifié sera passé au testeur. Les fichiers d'entrée à tester, s'il y en a, doivent toujours être spécifiés.
tester_library
Nom d'une bibliothèque avec son extension, entre guillemets. Une bibliothèque peut avoir une extension dll ou ex5. Les bibliothèques devant être testées sont définies automatiquement. Cependant, si l'une des bibliothèques est utilisée par un indicateur personnalisé, cette propriété est requise
Pour chaque bloc de paramètres, une empreinte numérique sous forme de hachage MD5 est créée et est envoyée à l'agent. Le hachage MD5 est unique pour chaque ensemble, son volume est bien plus petit que la quantité d'informations sur laquelle il est calculé.
L'agent reçoit un hachage de blocs et les compare à ceux qu'il possède déjà. Si l'empreinte du bloc de paramètres donné n'est pas présente dans l'agent, ou si le hachage reçu est différent de celui existant, l'agent demande ce bloc de paramètres. Cela réduit le trafic entre le terminal et l'agent.
Après le test, l'agent renvoie au terminal tous les résultats de l’exécution, qui sont affichés dans les onglets « Résultats du test » et « Résultats d'optimisation » : le bénéfice reçu, le nombre de transactions, le coefficient de Sharpe, le résultat de la fonction OnTester(), etc.
Lors de l'optimisation, le terminal distribue les tâches de test aux agents dans de petits paquets, chaque paquet contient plusieurs tâches (chaque tâche signifie un test unique avec un ensemble de paramètres d'entrée). Cela réduit le temps d'échange entre le terminal et l'agent.
Les agents n'enregistrent jamais sur le disque dur les fichiers EX5, obtenus du terminal (EA, indicateurs, bibliothèques, etc.) pour des raisons de sécurité, de sorte qu'un ordinateur avec un agent en cours d'exécution ne puisse pas utiliser les données envoyées. Tous les autres fichiers, y compris les DLL, sont enregistrés dans le bac à sable. Dans les agents distants, vous ne pouvez pas tester les EA à l'aide de DLL.
Les résultats des tests sont ajoutés par le terminal dans un cache de résultats spécial (le cache de résultats), pour y accéder rapidement, au besoin. Pour chaque ensemble de paramètres, le terminal recherche dans le cache des résultats les résultats déjà disponibles des exécutions précédentes, afin d'éviter des doubles d’exécutions. Si le résultat avec un tel ensemble de paramètres n'est pas trouvé, l'agent se voit confier la tâche d'effectuer le test.
Tout le trafic entre le terminal et l'agent est crypté.
Utilisation du dossier partagé de tous les terminaux clients
Tous les agents de test sont isolés les uns des autres et du terminal client : chaque agent a son propre dossier dans lequel ses journaux sont enregistrés. De plus, toutes les opérations sur les fichiers pendant le test de l'agent se produisent dans le dossier agent_name/MQL5/Files. Cependant, nous pouvons implémenter l'interaction entre les agents locaux et le terminal client via un dossier partagé pour tous les terminaux clients, si lors de l'ouverture du fichier vous spécifiez le drapeau FILE_COMMON :
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- the shared folder for all of the client terminals common_folder=TerminalInfoString(TERMINAL_COMMONDATA_PATH); //--- draw out the name of this folder PrintFormat("Open the file in the shared folder of the client terminals %s", common_folder); //--- open a file in the shared folder (indicated by FILE_COMMON flag) handle=FileOpen(filename,FILE_WRITE|FILE_READ|FILE_COMMON); ... further actions //--- return(0); }
Utilisation des DLL
Pour accélérer l'optimisation, nous pouvons utiliser non seulement des agents locaux, mais également des agents distants. Dans ce cas, il existe certaines limitations pour les agents distants. Tout d'abord, les agents distants n'affichent pas dans leurs journaux les résultats de l'exécution de la fonction Print(), , les messages d'ouverture et de fermeture de positions. Un minimum d'informations est affiché dans le journal afin d'éviter que des EA incorrectement écrites n'endommagent l'ordinateur, sur lequel l'agent de suppression travaille, avec des messages.
Une deuxième limitation - l'interdiction d'utiliser des DLL lors du test des EA. Les appels DLL sont absolument interdits sur les agents distants pour des raisons de sécurité. Sur l'agent local, les appels de DLL dans les EA testés ne sont autorisés qu'avec l'autorisation appropriée « Autoriser l'importation de DLL ».
Fig. 10. L'option "Autoriser l'importation de DLL" dans les programmes mql5
Remarque : lorsque vous utilisez les EA reçus (scripts, indicateurs) qui nécessitent l'autorisation des appels DLL, vous devez être conscient des risques que vous assumez en autorisant cette option dans les paramètres du terminal. Indépendamment de la façon dont l'EA sera utilisé - pour les tests ou pour l'exécution sur un graphique.
Conclusion
L'article traite des principes fondamentaux, dont la connaissance vous aidera à maîtriser rapidement le test des EA dans le terminal client MetaTrader 5 :
- Trois modes de génération de traits ;
- Calcul des valeurs des indicateurs pendant les tests ;
- Tests multi-devises ;
- Simulation de la durée des tests ;
- Le travail des fonctions OnTimer(), OnSleep() et IndicatorRelease() dans le Testeur de stratégie ;
- Le travail des agents de test lors des appels DLL ;
- Utilisation du dossier partagé pour tous les terminaux clients ;
- Synchronisation des barres en mode « Prix d’ouverture uniquement » ;
- Gestion d’événements.
La tâche principale du testeur de stratégie du terminal client est d'assurer l'exactitude requise des données avec le minimum d'effort du programmeur de MQL5. Les développeurs ont abattu un travail énorme pour que vous n'ayez pas à réécrire votre code juste pour tester votre stratégie de trading sur des données historiques. Il suffit de connaître les principes fondamentaux du test, et un EA correctement écrit fonctionnera aussi bien dans le testeur qu’en mode en ligne sur le graphique.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/239
- 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