Le Lecteur de Trading Axé sur l'Historique des Deals
Voir une Fois Vaut Mieux Qu' Entendre Deux Fois
L'analyse visuelle de l'historique du trade est une partie importante du travail analytique d'un trader S'il n'en était pas ainsi, il n'y aurait pas d'analyse technique qui transforme le monde des chiffres en monde des images. Eh bien, c'est clair, puisque 80% de la perception humaine se fait avec les yeux. La statistique, qui généralise l'information, ne peut pas dire beaucoup de nuances. Et seule la visualisation avec sa perception intuitive du monde des chiffres peut franchir les bornes. On dit qu'il vaut mieux voir une fois qu'entendre deux fois.
Dans cet article, nous n'allons pas examiner comment rédiger un Expert Advisor destiné à l'automatisation de l'affichage visuel de l'historique du trade. Nous allons aborder les questions de transmission d'informations entre objets, de planification de grandes applications , de gestion de graphiques, de synchronisation d'informations de différents symboles, etc.
Traditionnellement, je souhaite d'abord vous parler des avantages de l'application du lecteur et du script associé, puis nous passerons à l'analyse du code.
Exécuter des deals dans le testeur de stratégie MetaTrader 5
Le fonctionnement du lecteur est basé sur le rapport HTML MetaTrader 5. Ainsi, l'historique de l'Automated Trading Championship 2010 peut être obtenu en se connectant à un compte nécessaire de l' ATC-2010 et en enregistrant l'historique des trades sous forme de rapport HTML.
Le serveur duAutomated Trading Championship 2008 étant arrêté, nous ne sommes pas en mesure de le faire de la même manière. Le site comporte le rapport général de tous les concurrents emballés dans une seule archive zip. Automated_Trading_Championship_2008_All_Trades.zip
L'archive "Automated Trading Championship 2008 All Trades.zip" doit être décompressée dans le dossier \Files du répertoire d'installation de MetaTrader 5.
Pour analyser l'historique duAutomated Trading Championship 2008, vous devez exécuter le rapport Parser MT4 qui analysera l'historique, effectuera une sélection pour la connexion indiquée et l'enregistrera dans un fichier binaire. . Ce fichier binaire est lu par lePlayer Report de l’Expert Advisor
Le Player Report EA doit être exécuté dans le testeur de stratégie avec l’identifiant nécessaire indiqué. Une fois le test terminé, enregistrez un rapport au format HTML. L’identifiant indiqué n'affecte pas le résultat du test, mais il sera affiché dans le rapport en tant que paramètre d'entrée "identifiant". Cela permet de discerner davantage les rapports. Étant donné que les rapports sont créés par le même Expert Advisor, il est recommandé de leur donner des noms différents de celui par défaut.
Le scriptReport Parser MT4 dispose aussi du paramètre d'entrée "identifiant" où vous devez indiquer l’identifiant d'un concurrent dont vous souhaitez voir l'historique. Si vous ne connaissez pas l’identifiant d'un concurrent, mais que vous connaissez le pseudonyme, démarrez le script avec la valeur zéro (par défaut) du identifiant. Dans ce cas, le script ne fera pas de sélection par identifiant ; il créera simplement un fichier csv où tous les identifiants sont répertoriées par ordre alphabétique. Le nom du fichier est"Automated Trading Championship 2008 All Trades_plus". Dès que vous trouvez un participant nécessaire dans ce fichier, exécutez à nouveau le script avec le identifiant indiqué.
Ainsi le tandem du script Report Parser MT4 et le Player Report EA créé un rapport html standard MetaTrader 5 Strategy Tester sur la base d’un historique du trade dans le format MetaTrader 4.
Le Player Report Expert Advisor n'exécute pas les transactions exactement comme elles ont été exécutées dans la réalité, il le fait approximativement. Les raisons en sont les différentes cotations, l'arrondi du temps aux minutes dans le rapport et les dérapages lors de l'exécution. Dans la plupart des cas, la différence est de plusieurs points, cela se produit dans 10% des trades. Mais il suffit de diminuer le profit dans le testeur de stratégie de ~170 000 à ~160 000, par exemple. Tout dépend du volume de deals avec dérapage.
Fonctionnement du Lecteur
Comme je l'ai mentionné précédemment, le lecteur peut être utilisé pour regarder l'historique des échanges duAutomated Trading Championship 2008 à l'aide d'applications supplémentaires et duAutomated Trading Championship 2010 peut être regardé directement.
De plus, le lecteur prend en charge tous les rapports MetaTrader 5, vous pouvez donc regarder l'historique des transactions de n'importe quel Expert Advisor exécuté dans le testeur de stratégie ou l'historique des transactions manuelles qui n'est pas formaté par le testeur, mais enregistré sous forme de rapport à partir de l'"Historique " de la fenêtre "Boîte à outils".
Paramètres de Player History Trades exp v5 Expert Advisor:
- Nom du fichier HTML du strategy tester report
- liste des graphiques nécessaires;
- supprimer les graphiques lors de la suppression de l’ EA;
- début et fin de l'historique;
- période du générateur;
- police des commentaires sur les deals ;
- taille de police pour le commentaire aux offres ;
- couleurdes opérations d'achat et de vente ;
- nombre de vitesses;
- taille verticale du bouton de progression.
Un rapport du testeur de stratégie MetaTrader 5 est utilisé comme fichier d'entrée pour le lecteur de l'historique des deals. C'est le nom d'un fichier de rapport qui doit être indiqué comme paramètre d'entrée du Player History Trades exp v5 EA "nom du fichier html du rapport du testeur de stratégie". Lors du démarrage du lecteur, un utilisateur est en mesure d’indiquer une période de lecture dans les variables d'entrée "début de l'historique" et "fin de l'historique".
Si ces variables ne sont pas définies, le lecteur les retirera de l'historique des deals à partir du premier deal et se terminant par le dernier deal. Le nombre de symboles utilisés pour le trading ne fait aucune différence. Seule l'heure du premier et du dernier deal sur le compte est prise en compte.
De plus, un utilisateur peut définir les noms des symboles dont les graphiques doivent être analysés. Les noms doivent être indiqués sous forme d'énumération dans la variable « liste des graphiques nécessaires ». L'analyse de cette variable n'est pas sensible à la case et au type de séparateur. Si la variable n'est pas définie, tous les symboles échangés sur le compte sont ouverts. Et parfois il y en a beaucoup.
Par exemple,Manov a utilisé 12 paires de devises dans son trading. Je recommande de ne pas définir plus de quatre symboles à la fois. Premièrement, il est pratique de les organiser; deuxièmement, de nombreux graphiques ralentissent la vitesse de lecture. Chaque symbole est traité dans la boucle générale, l'augmentation du nombre de symboles entraîne un ralentissement de la génération des ticks.
Le lecteur fonctionnera également même si vous indiquez un symbole qui n'a pas été utilisé dans le trading. Dans ce cas, le graphique n'affichera aucun deal, ce sera comme les autres graphiques. De plus, il aura l'indicateur de solde attaché; cependant, il n'affichera que l'historique du solde général dans n'importe quelle de ses variantes.
J'ai volontairement ignoré la description du paramètre "Supprimer le graphique lors de la suppression de l'EA". Il concerne le comportement de l'Expert Advisor, pas sa gestion. Le fait est que l'Expert Advisor analyse beaucoup d'informations pour son fonctionnement. J'ai décidé que certaines informations dont dispose l'EA seront utiles pour l'analyse sous forme de fichiers. L'Expert Advisor crée des fichiers csv qui comportent les opérations de trading pour chaque symbole et le fichier avec les soldes synchronisés de tous les symboles, ce qui peut être utile pour détecter un symbole dans un panier multi-devises.
La même variable est également utilisée pour la suppression des graphiques automatiquement ouverts par l'Expert Advisor. Traditionnellement, l'AE doit nettoyer son lieu de travail en fin d'opération. Mais si un utilisateur souhaite analyser de près un graphique sans le contrôle EA, il doit démarrer l'EA avec le paramètre "supprimer les graphiques lors de la suppression de l'EA" défini sur "false".
Les paramètres suivants ne sont pas si importants.
La période du générateur définit le paramètre initial du générateur de tick. Le terme « tick » n'est pas utilisé ici dans son sens classique ; cela signifie la variation de niveau. Dans l'Expert Advisor, les ticks sont générés selon quatre points de barres. Le paramètre "Période du générateur" définit l'état initial du générateur. De plus, vous pourrez modifier ce paramètre dans le lecteur pendant qu'il fonctionne.
Pourquoi ne générons-nous pas toutes les périodes à partir de M1 ? Pourquoi devons-nous modifier la période du générateur? Le problème est que les barres des périodes plus longues comportent beaucoup de barres M1, nous devrions peut-être accélérer le processus de génération. C'est pourquoi la possibilité de modifier la période est implémentée. Tous les délais ne sont pas implémentés dans le générateur, seulement certains d'entre eux. La manière de le modifier dans le code sera décrite plus tard.
Le paramètre "police des commentaires sur les deals" peut être utile, par exemple, lorsque les commentaires sur les deals empêchent de visualiser les deals eux-mêmes. Si vous définissez la taille sur 1, l'inscription ressemblera à une ligne fine et ne gênera pas la visualisation. À cela, vous pourrez voir le volume du deal et la position dans l'onglet "Liste des objets" lorsque vous découvrirez le nom de l'objet dans l'info-bulle.
L'historique des deals est tracé avec des deals distincts, mais la ligne tracée dépend du type de position.
En utilisant la "couleur des opérations d'achat" et la "couleur des opérations de vente", vous pouvez définir les couleurs que vous souhaitez.
Sur la capture d'écran ci-dessus, vous pouvez voir que le niveau de position diffère souvent du niveau du deal.
Mais le profit est calculé sur la base du niveau de position. J'ai donc décidé d'afficher la position avec une ligne de tendance et de relier les niveaux de position et de traiter à l'aide d'une ligne verticale. Le commentaire à côté du niveau de position affiche les informations suivantes :
[deal volume|position volume]
Si le type de deal ne correspond pas au type de position (par exemple, il y a une clôture partielle), alors les volumes seront affichés avec des signes supplémentaires :
[<deal volume>|position volume]
En premier lieu, vous pouvez voir le volume du deal tel qu'il est affiché dans le rapport du trade ; le volume de position est calculé sur la base de l'état antérieur de la position et des modifications apportées par la deal.
Le paramètre "nombre de vitesses" régule le nombre d'étapes de diminution de la vitesse de lecture. Le lecteur démarre à vitesse maximale. De plus, vous pourrez le diminuer et l'augmenter dans la valeur du paramètre "nombre de vitesses". Ainsi le bouton de vitesse et la période du générateur forment une gamme complète d'outils pour gérer la vitesse de lecture de l'historique du trade..
Et le dernier paramètre est la "taille verte du bouton de progression". Je l'ai fait pour les utilisateurs qui préfèrent les gros boutons de la barre de progression. D'une manière générale, mon objectif était d'éviter de cacher le graphique derrière les commandes. C'est pourquoi le paramètre "taille verte du bouton de progression" est fixé à 8.
Passons maintenant aux commandes du lecteur.
La vitesse est contrôlée à l'aide des flèches gauches et droites. Le mode de contrôle dépend de l'état du bouton central (carré). Dans l'état non enfoncé, il modifie la vitesse, dans l'état enfoncé, il modifie la période du générateur.
L'objet de contrôle de l' indicateur de solde est affiché sous la forme d'un ovale plein, mais en réalité il est composé de deux gros boutons qui dépassent largement les limites de sa taille visuelle. Le bouton gauche est destiné à ajouter et supprimer l'indicateur de solde du graphique, et le bouton droit contrôle le contenu des données.
Dans l'état "Tous", les informations sur le solde total du compte sont affichées; l'état "Somme" est destiné à afficher un échantillonnage du solde pour le symbole du graphique sur lequel l'indicateur est exécuté. Le contrôle de l'indicateur est asynchrone, ce qui indique que l'indicateur peut être exécuté sur un graphique et non sur un autre.
L'objet de contrôler l'indicateur de solde est la seule exception dans la synchronisation des graphiques ; tous les autres objets des champs sont synchronisés. En d'autres termes, les modifications apportées à un symbole sont automatiquement apportées aux autres symboles.
Le plays/stop affiche l'opération qui sera effectuée dès que vous appuyez dessus. Pendant la lecture, deux petites lignes s'affichent indiquant que l'opération sera interrompue si vous appuyez dessus. Et vice versa, le triangle est affiché à l'état de pause. Ainsi si vous appuyez dessus, le lecteur commencera son opération.
La Ligne de progressionse compose de 100 boutons de commande utilisés comme déclencheurs - si un bouton est enfoncé, tous les autres boutons ne sont plus enfoncés. Comme il y a 100 boutons, la période de lecture est divisée en 100 parties. Si le nombre de barres n'est pas divisible par 100, le reste est ajouté à la dernière partie. C'est pourquoi les paramètres incluent les paramètres « début de l'historique » et « fin de l'historique ». En modifiant ces paramètres, vous pouvez accéder à la période d'historique nécessaire.
En appuyant sur un bouton, un utilisateur modifie la date du générateur interne de ticks et navigue dans la barre zéro. S'il n'est pas enfoncé mais que l'heure interne du générateur s'est déjà déplacée en dehors du bouton actif, le lecteur effectuera lui-même la commutation correspondante.
Ainsi l'objet "ligne de progression" est à la fois l'indicateur de la progression et le contrôle actif de la navigation. Les objets de contrôle du lecteur sont automatiquement masqués et étendus au milieu du graphique ; ainsi, si vous devez appuyer sur un certain bouton de la ligne de progression, étendez le graphique en plein écran.
Parlons maintenant du comportement des graphiques gérés par le lecteur. Le lecteur effectue la synchronisation de tous les graphiques, mais cela ne signifie pas que tout changement d'échelle, de type de graphique, de schéma de couleurs, de décalage de la barre zéro, etc. effectué sur la graphique principale est répété sur les autres graphiques.
Les modifications comportent le changement de calendrier. Notons ici que le graphique considéré comme principal par le lecteur est celui où sont affichées les commandes, et non celui avec la ligne bleue d'activité. D’habitude, il s'agit d'un seul et même graphique, mais ce n'est pas toujours le cas. Pour activer un graphique, cliquez dessus dans le champ du graphique.
Il y a une fonction d'utilisation du lecteur. Si deux objets se trouvent dans le même champ, les boutons cessent de fonctionner. C'est pourquoi parfois, lorsque la ligne de soumission traverse le champ du lecteur, pour appuyer sur un bouton, vous devrez passer à un autre graphique ou modifier l'échelle verticale du graphique.
Préparer le Démarrage
- Pour que tout fonctionne, téléchargez player_history_trades.zip et décompressez-le dans le dossier terminal_data_folder/MQL5/Indicators.
- Ouvrez le dossier copié Player History Trades etcompilez quatre fichiers dans son répertoire racine dans MetaEditor. La séquence de compilation des fichiers n'a pas d'importance.
- Assurez-vous que la période d'historique requise pour tous les symboles du rapport de transaction est disponible sur la périodeМ1timeframe. Pour se faire, ouvrez manuellement le graphique nécessaire avec le délai M1, placez une ligne verticale et ouvrez laListe d’objets à l’aide de la clé de combinaison du menu contextuel Ctrl+B . Modifiez ensuite la date de la ligne verticale par la date de début du trading
- Appuyez ensuite sur le bouton "Afficher". S'il n'y a pas de cotations, il peut y avoir deux raisons à cela. Soit elles ne sont pas téléchargées, soit le paramètre "Max bars in chart" est trop petit. Pour le voir, accédez àTools->Options->Charts.
Maintenant, tout devrait fonctionner.
Début du Développement
Pour élaborer une application, vous devez avoir un plan, qui se transforme en un diagramme au fur et à mesure que vous l'étudiez, puis il se transforme en code. Mais le projet lui-même commence plus tôt. Le point de départ de tout projet est constitué par les propriétés d'application requises par l'utilisateur. Alors, quelles propriétés devrait avoir le lecteur de l'historique du trade ?
- Être multi-devises.
- Ouverture automatique des graphiques requis.
- Interface de navigation pratique et possibilité de faire défiler l'historique dans les deux sens.
- Affichage synchrone sur tous les graphiques.
- Début/Pause de la lecture.
- Une possibilité de choisir (et le mode par défaut) le nombre et les symboles des graphiques qui s'affichent.
- Une possibilité de choisir (et le mode par défaut) une période à laquelle le lecteur travaillera.
- Affichage de l'historique des deals sur un graphique.
- Affichage de l'historique du solde et des fonds propres
- Affichage séparé du solde (fonds propres) d'un symbole et du solde total (fonds propres) d'un compte.
Les quatre premiers éléments déterminent le concept général. Les autres propriétés déterminent le sens d'implémentation des méthodes.
Le plan général de fonctionnement du lecteur :
- Chargez un rapport HTML ;
- Analysez-le pour traiter et restaurer l'historique des positions ;
- Préparer les deals sous forme de file d'attente des commandes pour l'ouverture/la clôture ;
- Sur simple commande de l'utilisateur, lancer l'affichage de la dynamique de l'historique des deals avec calcul des taux nécessaires sous forme d'indicateurs (graphiques de fonds propres, prélèvements, etc.) ;
- Organiser l'affichage d'un panneau d'information sur le graphique avec les autres taux.
De plus, un Expert Advisor spécial est requis pour le trading dans le testeur de stratégie selon un rapport MetaTrader 4:
- Les deals analysés doivent être écrits sous forme de fichier de données binaires pour l'Expert Advisor ;
- Créez le rapport du testeur de stratégie MetaTrader 5.
C'est le schéma général de démarrage du développement, une spécification des besoins. Si vous l'avez, vous pouvez planifier l'écriture du code de haut en bas, du concept à l’implémentation de la fonctionnalité.
Pour ne pas étendre l'article, je vais décrire plus loin uniquement les parties les plus importantes du code. Vous ne devriez pas rencontrer de problèmes lors de la lecture du code car il est bien commenté.
Commandes et Deals
Actuellement, il existe deux concepts de trade. L'ancien concept utilisé dans MetaTrader 4, et celui utilisé pour les soumissions réelles et utilisé dans MetaTrader 5, appelé concept de "netting". La description détaillée de la différence entre eux est donnée dans l'articleCommandes, Positions, et Deals dans MetaTrader 5
Je vais décrire une seule grande différence. Dans MetaTrader 4, une commande peut être représentée comme un conteneur qui stocke les informations sur l'heure d'ouverture, le prix d'ouverture et le volume du trade. Et pendant que la porte du conteneur est ouverte, elle est dans l'état actif de trade Dès que vous fermez le conteneur, toutes les informations qu'il comporte sont déplacées dans l'historique.
Dans MetaTrader 5, les positions sont utilisées comme de tels conteneurs. Mais la différence notable est l'absence d'historique des positions. Il n'y a que l'historique commun des commandes et des deals. Et bien que l'historique comporte toutes les informations nécessaires pour restaurer l'historique des positions, vous devez consacrer du temps à la réorganisation de la réflexion.
Vous pouvez savoir à quelle position appartient une commande ou un deal sélectionné en utilisantORDER_POSITION_ID et DEAL_POSITION_ID respectivement les identifiants Ainsi, pour convertir l'historique dans un format adapté à MetaTrader 5, je diviserai les commandes de l'historique MetaTrader 4 en deux trades distincts - les trades d'ouverture et de clôture.
L'analyseur HTML
Pour ceux qui ne connaissent pas l'argot informatique, je vais décrire ce que signifie le mot parse. L'analyse syntaxique désigne l'analyse syntaxique (grammaticale ou lexicale) d'un texte ou de toute séquence de lexèmes (symboles, mots, octets, etc.) qui vérifie la correspondance du texte d'entrée avec une grammaire indiquée et compose un arbre d'analyse selon lequel vous pouvez effectuer d'autres calculs ou transformations.
Deux grandes classes CTable et CHTML sont utilisées dans l'analyseur. L'utilisation de la classe CTable est décrite en détail dans l'article Tableaux électroniques en MQL5, c'est pourquoi je ne vais pas la décrire à nouveau.
Pour l'analyse HTML, j'ai élaboré la classe CHTML. Selon ma première idée, sa description aurait dû devenir un article. Mais la classe est trop simple pour écrire un article, je vais donc en donner une description concise.
Le concept général de la classe peut être décrit par le terme « balise ». Une étiquette peut être représentée comme une fonction avec des enclos. Par exemple, balise(en-tête, coffret), où « header » est le titre de la balise (les variables de balise qui contrôlent l'apparence de la page y sont généralement indiquées) et « coffret » est le contenu du conteneur de balises. Ces balises constituent l'intégralité du langage HTML.
La structure générale de la classe peut être représentée comme un appel d'objets en trois étapes. L'instance de la classe CHTML crée les objets de toutes les balises possibles dans son corps. Les fonctions des balises sont créées par un modèle et elles ne diffèrent les unes des autres que par leurs noms et les paramètres de deux indicateurs.
Un indicateur détermine la présence de l'en-tête et l'autre détermine la présence du coffret. Ces indicateurs permettent de présenter toutes les balises avec une structure commune. Chaque instance de balise crée une instance de la classe CTegs dans son corps. Cette classe comporte des méthodes communes pour toutes les balises et effectue les principales opérations de recherche d'une balise nécessaire dans le corps d'un document.
Voici donc à quoi ressemble l'appel en trois étapes :
h.td.base.casket
Cette inscription signifie que l'objet 'h' appelle la valeur de la variable 'coffret ' via l'objet 'td' (c'est-à-dire une instance de la balise <td header >coffret </td>) via l'objet imbriqué 'base' ( c'est un objet de la classe CTegs).
La classe comprend également des méthodes de recherche des balises, elles sont combinées dans la méthode publique
h.td.Search(text,start);
qui renvoie le point de recherche de la fin de la balise et remplit les variables 'header' et 'coffret ' de la balise.
Les autres fonctions préparées dans la classe ne sont pas utilisées, donc je ne vais pas les décrire, il y a beaucoup d'autres choses intéressantes à raconter.
À la fin de la description du travail avec des documents HTML, je tiens à mentionner que deux types d'analyseurs sont utilisés dans l'article ; ils ne diffèrent que par le type de sauvegarde des informations obtenues à partir du fichier. Le premier type utilise la sauvegarde de l'intégralité du document dans une seule variable de type 'string', elle est utilisée dans le lecteur. Le deuxième type utilise l'analyse syntaxique ligne par ligne du rapport. Il est utilisé dans le script pour préparer l'historique du Championnat 2008.
Pourquoi utilise-je deux approches ? La question est pour le bon fonctionnement des fonctions de la classe CTegs, la balise entière doit être placée dans la chaîne analysée. Et ce n'est pas toujours possible. Par exemple, dans le cas de balises comme tableau, html, body (elles sont multilignes). Une variable de type chaîne permet de stocker (d'après mes calculs) 32750 symboles sans les symboles de tabulation. Et avec '\r' (après chaque 32748-ème symbole) j'ai réussi à stocker jusqu'à 2 000 000 de symboles ; après avoir atteint cette valeur, j'ai arrêté mes tentatives. Très probablement, plus de symboles peuvent être stockés.
Alors pourquoi utilisons-nous deux approches? Le point est pour un parseur universel du lecteur dont vous avez besoin pour trouver un tableau approprié. Les tableaux requis pour le rapport du testeur et pour le rapport de l'historique des deals se trouvent à des endroits différents. Pour garder la polyvalence (pour que l'analyseur comprenne les deux rapports), j'utilise le schéma de recherche dans le tableau avec la balise 'td' contenant "deals".
La structure du rapport du Championnat 2008 est connue et il n'est pas nécessaire de chercher dans le tableau nécessaire. Cependant, le document du rapport est énorme (35 Mo), et placer l'ensemble du rapport sur une seule variable prendrait beaucoup de temps. Cette situation stipule la deuxième approche de l'analyse.
Le Lecteur
10 exigences pour le lecteur sont décrites dans la section "Démarrage du Développement". Puisque le multi-devises est à la première place, l'Expert Advisor doit gérer les graphiques. Il sera logique que chaque graphique soit traité par un objet distinct qui dispose de toutes les fonctionnalités nécessaires au fonctionnement du lecteur.
Puisque nous travaillons avec l'historique, nous avons besoin d'un exemple distinct d'historique pour un fonctionnement ininterrompu au lieu d'espérer pouvoir l'obtenir à tout moment. De plus, l'obtention répétée du même historique serait un gaspillage par rapport au fait de le conserver dans le lecteur. Au final, le schéma suivant ressort :
La Programmation orientée-objet(OOP) permet d'écrire des applications assez volumineuses à l'aide de systèmes de blocs. La partie élaborée du code de l'Expert Advisor peut être préalablement écrite dans un script, déboguée puis connectée à l'Expert Advisor avec un minimum d'adaptation.
Un tel schéma de développement est pratique, car vous êtes sûr que le code connecté ne comporte pas d'erreurs (car il fonctionne dans le script sans erreurs), et tous les bogues trouvés sont des erreurs d'adaptation. Il n'y a pas un tel avantage lorsqu'un code est écrit de bas en haut, lorsque vous décrivez tout au même endroit comme une procédure. Et un nouveau bug peut apparaître à n'importe quel endroit de l'application.
Ainsi, la programmation de haut en bas présente les avantages de simplicité et de rapidité d'écriture d'une application. Vous pouvez demander "qu'est-ce qui est simple ici ?", je répondrais par une allégorie - il est difficile d'apprendre à faire du vélo, mais une fois que vous l'aurez appris, vous ne remarquerez même pas ce processus. Vous apprécierez simplement une conduite rapide. Une fois que vous aurez appris la syntaxe de la POO, vous obtiendrez un grand avantage.
Pour continuer la narration, je dois décrire trois termes de POO : Association, Agrégation et Composition.
- L'Association indique une connexion entre des objets. L'Agrégation et la Composition sont des cas particuliers de l'association.
- L'Aggregation implique que les objets sont connectés au rapport "partie de". L'agrégation peut être multiple, c'est-à-dire qu'un objet peut être agrégé en plusieurs classes ou objets.
- La Composition est une variante plus stricte de l'agrégation. En plus de l'exigence de "partie de", cette "partie" ne peut pas appartenir à différents "propriétaires" simultanément, et elle est supprimée lorsque le propriétaire est supprimé.
Puisque l'association comprend l'agrégation et la composition, lors d'une analyse détaillée, tous les cas qui ne peuvent être qualifiés d'agrégation ou de composition sont appelés association. Généralement, les trois idiomes sont appelés association.
class Base { public: Base(void){}; ~Base(void){}; int a; }; //+------------------------------------------------------------------+ class A_Association { public: A_Association(void){}; ~A_Association(void){}; void Association(Base *a_){}; // At association, data of the bound object // will be available through the object pointer only in the method, // where the pointer is passed. }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class A_Aggregation { Base *a; public: A_Aggregation(void){}; ~A_Aggregation(void){}; void Aggregation(Base *a_){a=a_;}; // At aggregation, data of the bound object // will be available through the object pointer in any method of the class. }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class A_Composition { Base *a; public: A_Composition(void){ a=new Base;}; ~A_Composition(void){delete a;}; // At composition, the object becomes the class member. };
Il existe une fonction dans MQL5 pour transmettre un pointeur via un paramètre :
GetPointer(pointer)
Son paramètre est le pointeur d'objet.
Par exemple :
void OnStart() { Base a; A_Association b; b.Association(GetPointer(a)); }
Les fonctions appelées dansOnInit() de mon code utilisent souvent l'association. La composition est appliquée dans la classe CHTML. Et j'utilise l'agrégation et la composition ensemble pour lier des objets au sein de la classe CPlayer. Par exemple, en utilisant l'agrégation, les objets des classes CChartData et SBase créent un champ de données commun pour tous les objets créés à l'aide de la composition dans le lecteur.
Visuellement, il peut être représenté comme suit :
Les classes, dont les objets sont créés de manière composite dans la classe CPlayer, disposent d’une structure de modèle avec une extension supplémentaire des fonctionnalités. L'utilisation des modèles est décrite dans l'article Utilisation de pseudo-modèles comme alternative aux modèles C++, je ne vais donc pas en donner une description détaillée ici.
Un modèle pour la classe ressemble à ceci :
//this_is_the_start_point //+******************************************************************+ class _XXX_ { private: long chart_id; string name; SBase *s; CChartData *d; public: bool state; _XXX_(){state=0;}; ~_XXX_(){}; void Create(long Chart_id, SBase *base, CChartData *data); void Update(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void _XXX_::Create(long Chart_id, SBase *base, CChartData *data) { chart_id=Chart_id; s=base; // binding data to the player structure d=data; // binding data to the chart structure name=" "+ChartSymbol(chart_id); if(ObjectFind(chart_id,name)<0)// if there is no object yet {//--- try to create the object if(ObjectCreate(chart_id,name,OBJ_TREND,0,0,0,0,0)) {//--- } else {//--- failed to create the object, tell about it Print("Failed to create the object"+name+". Error code ",GetLastError()); ResetLastError(); } } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void _XXX_::Update() { }; //+******************************************************************+ //this_is_the_end_point
J'ai donc créé les classes vides par le modèle, les ai connectées, vérifié si elles traitaient toutes les demandes correctement, et ce n'est qu'après cela que j'ai commencé à remplir les classes composites avec les fonctionnalités nécessaires. C'est ce qu'on appelle la programmation descendante. En cas de panne, vous savez où chercher sa cause.
Maintenant que le concept général de construction est clair, nous pouvons passer aux détails.
Tout d'abord, examinons le fonctionnement des fonctions, déclaré dans le Expert Advisor Player History Trades exp v5.
La fonction OnInit() prépare habituellement les informations. Il crée un objet de la classe CParser_Tester qui analyse un rapport du testeur de stratégie, obtient la liste de tous les instruments financiers échangés, traite les deals, calcule les volumes et les niveaux de positions, puis trace l'historique sur le graphique. Le dernier élément décrit la raison pour laquelle l'objet n'est pas supprimé juste après la transmission des données. Le problème est que l'information est préparée avant l'ouverture des graphiques. Et les objets graphiques nécessitent un ID de graphique pour le traçage. C'est pourquoi l'objet de la classe CParser_Tester est supprimé ultérieurement.
De plus, comme nous disposons des noms des symboles utilisés pour le trading, la fonction Balance_Process() est appelée, elle calcule les soldes et les capitaux propres de tous les symboles qui lui sont transmis ainsi que le solde total et les capitaux propres sur la base de l'historique M1.
Dans cette partie; l'application est particulièrement sensible au manque d'informations ; c'est pourquoi j'ai mis en place une interruption du fonctionnement de l'EA au cas où les informations pour l'un des symboles ne seraient pas téléchargées. Lorsque l'application arrête de s'exécuter, elle affiche une alerte avec les informations du symbole pour lequel elle doit être téléchargée.
Le résultat du travail de la fonction Balance_Process() sont des fichiers binaires de l'historique du solde et des capitaux propres sur M1 qui sont ensuite découpés en périodes nécessaires par l'indicateur de solde. Bon, je suis un peu en avance, le fonctionnement de l'indicateur de solde sera décrit plus tard.
La prochaine étape du démarrage de l'Expert Advisor est la sélection des symboles. À cet endroit, nous analysons le paramètre d'entrée « liste des graphiques requis » ; si le symbole nécessaire se trouve dans la listeMarket Watch", ajoutez-le au tableau de symboles. De cette façon, nous nous protégeons des « imbéciles », car un utilisateur peut indiquer un abracadabra au lieu d'un nom de symbole ou l'imprimer de manière erronée.
Comme nous avons la liste vérifiée des symboles demandée par un utilisateur pour l'ouverture, nous pouvons ouvrir les graphiques. Cela se fait à l'aide de la fonction suivante :
ChartOpen(symbol,period)
Cette fonction ouvre un graphique avec le symbole et la période qui lui sont transmis en paramètres. En théorie, cette fonction renvoie l' ID du graphique ouvert mais cela n'arrive pas toujours.
En raison de la perte de l' ID, l'application fonctionne mal. Pour l'éviter, j'ai créé deux fonctions :
ChartTotal(arrayID); // get the list of charts before opening additional charts CurrentChart(arrayID); // get the list of chart for operation
Une fonction est exécutée avant d'ouvrir les graphiques, et l'autre est exécutée après. La fonction ChartTotal() obtient la liste des graphiques qui ont été ouverts avant le début de l'EA (y compris le graphique sur lequel l'EA est exécuté) et enregistre leurs identifiants dans le tableau d'entrée.
La fonction CurrentChart() obtient ces informations, crée une nouvelle liste en considérant les graphiques déjà ouverts ; puis selon la différence des listes, il passe les identifiants des graphiques créés par l' EA au tableau paramétrique. Ce schéma est fiable, car il fonctionne selon le fait de l'ouverture du graphique.
Maintenant que nous avons les identifiants des graphiques nécessaires, nous pouvons les prendre sous contrôle. Pour se faire, parcourez tous les graphiques en boucle, et en utilisant l'objet du CParser_Tester (comme vous vous en souvenez, j'ai dit précédemment que nous en aurions besoin) tracez l'historique des deals et créez les objets pour gérer le graphique.
La dernière action dans OnInit() - créer le minuteur et l'appeler pour qu'il fonctionne. Toutes les autres actions seront effectuées dans OnTimer().
Le premier problème de création du lecteur apparaît au stade initial de développement. C'est le problème de la création d'un minuteur La fonctionEventSetTimer(timer) permet de créer une minuterie avec une fréquence d'au moins 1 seconde. Avec cette variante, les ticks seraient générés une fois par seconde. Même avec la limitation de la vision humaine, une seconde est trop longue. J'ai besoin d'au moins 100 millisecondes.
C'est pourquoi j'ai implémenté une boucle à l'intérieur du minuteur ; il sort plusieurs millisecondes avant l'arrivée d'un nouvel événement Timer. Mais cette implémentation a rendu impossible de nombreuses solutions techniques. Par exemple, la possibilité de recevoir des événements disparaît, car ils attendent constamment que la minuterie sorte du cycle. Et l'impossibilité de recevoir des événements fait fondre la possibilité de placer les objets des lecteurs en indicateur et d'effectuer simultanément des calculs parallèles de tous les graphiques. Mais même avec un traitement conséquent des graphiques, l'Expert Advisor fonctionne assez rapidement.
L'événement d'activation du graphique est remplacé par la classe composite CClick, dont les objets créent un signal de changement du graphique actif en cours de traitement dans le cycle de la fonction Click(n). La fonction Click() est un déclencheur qui suit les modifications du bouton d'activation du graphique. S'il détecte que le bouton est enfoncé, il fait commuter tous les autres objets à l'état passif. Le bouton d'activation du graphique est toujours près de l'utilisateur, mais il n'est pas visible, car il a la taille du graphique entier, il est coloré en arrière-plan et il est en arrière-plan. Lorsqu'un graphique est activé, le bouton est déplacé derrière les bordures visibles du graphique, ce qui permet de voir les objets graphiques des commandes du lecteur, qui sont masqués par le bouton d'activation du graphique en mode passif.
De plus, comme nous avons détecté le graphique principal à l'aide de la fonction Click(), passez au calcul du mouvement temporel, appelez la fonction Progress(Time) du lecteur actif. Cette fonction effectue les calculs suivants : vérifie si un utilisateur effectue des actions de navigation - sinon, vérifie s'il est temps de passer à la barre suivante ; s'il est temps, il vérifie si la progression doit être déplacée vers la section suivante.
En fin de compte, lorsque nous quittons la fonction Progress(Time), le cycle dispose des informations sur l'heure actuelle, qui sont utilisées pour les calculs ultérieurs. Ensuite, les paramètres du diagramme actif sont copiés dans les diagrammes esclaves. Cela se fait à l'aide d'une boucle dans la fonction CopyPlayer(n). Après cela, dans la fonction Play(Time), allez à l'exécution de toutes les modifications qui doivent être apportées au graphique pour faire penser à un utilisateur que le temps bouge, que les cotations arrivent et que le trading est effectué.
Classes Composites du Lecteur.
- CArrayRuler* - stocke et recherche des informations pour un déplacement rapide entre les barres d'une période actuelle.
- CRuler* - stocke et recherche les informations d'historique M1 pour la génération de ticks.
- CSpeed - contrôle les paramètres de vitesse et la période du générateur de ticks.
- CProgress - combine tous les boutons de progression en un seul objet, surveille qu'un seul bouton est enfoncé, modifie les couleurs des boutons.
- CPlay - il est chargé de démarrer et d'arrêter le lecteur, il contrôle également l'indicateur de solde.
- CCliquez - il est en charge des signaux d'activation du graphique.
- CBackGround - l'objet masque la barre zéro aux utilisateurs, ainsi qu'il masque les futures barres lorsque le décalage du graphique par rapport à l'état de la bordure droite est activé.
- CBarDraw - trace la barre zéro en fonction de l'échelle et du type de graphique (barres, chandeliers ou ligne).
- CHistoryDraw - donne l'illusion à l'utilisateur que le dernier deal change en temps réel.
* - les classes ne comportent pas d'objets graphiques.
Comme je l'ai déjà mentionné, les objets des classes CChartData et SBase créent un champ de données commun pour tous les objets à l'intérieur du lecteur en utilisant l'agrégation. L'objet de la classe CChartData est utilisé pour le stockage et la mise à jour des informations sur le graphique ainsi que pour sa gestion. En vertu de gestion du graphique , nous entendons modifier ses paramètres en copiant les paramètres du graphique principal. C'est ainsi que s'effectue la synchronisation des graphiques. Un utilisateur fait juste un premier signal en modifiant les paramètres du graphique actif, puis plusieurs fonctions du lecteur font le reste des opérations de synchronisation.
Voici comment cela se fait :
La fonction CopyPlayer(n), décrite dans l'Expert Advisor, appelle la fonction CPlayer::Copy(CPlayer *base) dans une boucle en transmettant par association le pointeur au lecteur du graphique actif. Dans CPlayer::Copy(CPlayer *base) à partir du pointeur du lecteur, le pointeur de l'objet CChartData du lecteur actif est transmis par association. Ainsi, les informations sur l'état du graphique actif sont placées dans l'objet de la classe CChartData du graphique esclave pour être copiées. Après cela, les informations sont mises à jour dans la fonction CPlayer::Update(), où toutes les vérifications nécessaires sont effectuées et tous les objets sont commutés aux états nécessaires.
Auparavant, je vous avais promis de vous expliquer comment ajouter des périodes dans la liste des périodes disponibles du générateur. Pour se faire, ouvrez le fichier d'inclusion "Player5_1.mqh". Le tableau statique TFarray[] est déclaré au début du fichier. Un point nécessaire doit être ajouté à sa place dans l'énumération qui remplit le tableau, et n'oubliez pas de modifier la taille du tableau et la variable CountTF. Après cela, compilez l'Expert Advisor v5 de Player History Trades exp.
Les Graphiques de Solde et Prélèvement.
L'indicateur de solde est géré depuis l'objet de la classe CPlay. Il comporte des méthodes de contrôle et des boutons.
Les modalités de contrôle de l'indicateur sont :
Ind_Balance_Create(); // add the indicator IndicatorDelete(int ind_total); // delete the indicator EventIndicators(bool &prev_state); // send an event to the indicator StateIndicators(); // state of the indicator, state checks
Les méthodes d'ajout/suppression fonctionnent selon l'état du bouton name_on_balance. Ils utilisent les fonctions standard MQL5 IndicatorCreate() et ChartIndicatorDelete().
L'indicateur reçoit un événement et effectue des calculs situés dans la fonction OnChartEvent() de l'indicateur en fonction du code de l'événement. Les événements sont divisés en trois types.
Il s'agit de "mettre à jour l'indicateur", "calculer le solde total" et "calculer le solde d'un symbole". Ainsi lors de l'envoi d'événements, en fonction de l'état du bouton name_all_balance, l'utilisateur contrôle le type de calcul. Mais le code indicateur lui-même ne comporte aucune analyse de l'historique du trade, calcul de la position ou recalcul du profit. L'indicateur n'en a pas besoin.
L'indicateur de solde est destiné à afficher des données d'historique, il ne sert donc à rien de tout refaire à chaque fois que vous changez de type ou ajoutez/supprimez l'indicateur. L'indicateur lit le fichier binaire de données calculées pour la période M1, puis, en fonction de la période actuelle du graphique, divise les données.
Ce fichier binaire est préparé par la fonction Balance_Process() appelée dans OnInit(). Si l'utilisateur ajoute un symbole qui n'a pas été utilisé pour le trading et qu'il n'y a pas de fichier binaire correspondant, l'indicateur affichera,donc, l'historique du solde total dans les deux variantes.
Parlons maintenant du format des données transmises à l'indicateur. Pour répartir correctement les informations, il ne suffit pas de connaître les quatre points d'une barre (Open, High, Low et Close).
En plus de cela, vous devez savoir quel est le premier - haut ou bas. Pour restituer l'information, la fonction Balance_Process() utilise le même principe que le mode "1 minute OHLC" du testeur de stratégie - si le cours de clôture d'une barre est inférieur au cours d'ouverture, alors le deuxième point est le maximum, sinon c'est le minimum.
Le même schéma est utilisé pour le troisième point. En conséquence, nous obtenons le format des données (ouvert, 2e point, 3e point, fermé) où tout est cohérent et défini. Ce format est utilisé pour diviser l'historique des cotations M1. Et le résultat est utilisé pour le calcul de l'historique du solde et des fonds propres, en fonction de l'historique des trades analysé (dans le même format).
Conclusion
En conclusion, je tiens à dire que ce développement ne prétend pas être un visualiseur de testeur, même si nous pouvons l'utiliser de cette manière. Cependant, si les idées qui y sont implémentées semblent être utiles dans le vrai visualiseur, j'en serai ravi. L’élaboration du lecteur vise à aider les traders et les rédacteurs d'EA à se préparer au championnat à venir et à travailler dur pour analyser les stratégies du trade.
De plus, je tiens à dire que le langage MQL5 est un outil de programmation puissant qui permet d’implémenter des projets assez énormes. Si vous êtes toujours en train de lire cet article, vous avez probablement remarqué que le projet "player" se compose de près de 8000 lignes de code. Je ne peux pas imaginer écrire un tel code en MQL4, et le problème n'est pas de tout décrire avec des procédures. S'il y a un développement prêt, il peut être refait dans le style de procédure. Mais élaborer de tels projets à partir de zéro est vraiment difficile.
Bonne chance!
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/242
- 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