
Création de Panneaux de Contrôle Actifs dans MQL5 pour le Trading
Introduction
L'efficacité est primordiale dans un environnement de travail, notamment dans le travail des traders, où la vitesse et la précision jouent un rôle déterminant. Lors de la préparation du terminal au travail, chacun rend son poste de travail le plus confortable possible pour lui-même, afin de mettre en œuvre les analyses et d'entrer sur le marché dans les plus brefs délais. Mais la réalité est que les développeurs ne peuvent pas toujours satisfaire tout le monde et il est impossible de régler à son gré certaines fonctions.
Par exemple, pour un scalper, chaque fraction de seconde et chaque frappe de la touche « New Order » est importante, et le réglage ultérieur de tous les paramètres peut être critique dans le temps.
Alors comment trouver une solution ? La solution réside dans la personnalisation des éléments, puisque MetaTrader 5 fournit des composants aussi merveilleux tels que le "Bouton", "Modifier" et "Étiquette". Faisons-le.
2. Options de panneau
Tout d'abord, décidons quel type de fonctions est essentiel pour un panneau. Nous mettrons l'accent sur le trading, en utilisant le panneau et, par conséquent, inclurons les fonctions suivantes :
- Position d'ouverture
- Passation d'une commande en cours
- Modification de la position/commande
- Clôture du position
- Supprimer une commande en cours.
En outre, aucun mal ne sera fait en ajoutant la capacité de personnaliser le panneau de schéma de couleurs, les tailles de police et les paramètres d'enregistrement. Donnons une description plus détaillée de tous les éléments du futur panneau. Nous indiquerons le nom de l'objet, son type et la description de sa vocation, pour chacune des fonctions du panneau. Le nom de chaque objet commencera par "ActP" - ce sera une sorte de clé, indiquant que l'objet appartient au panneau.
2.1. Positions ouvertes
Ci-dessous, nous allons introduire tous les paramètres nécessaires à l'ouverture de la position, et l'implémenter en cliquant sur un bouton. Les lignes auxiliaires, activées en cochant une case, nous aideront à définir les niveaux de Stop Loss et de Take Profit. La sélection du type d'exécution se fera à l'aide des boutons radio.
Nom | Type | Description |
---|---|---|
ActP_buy_button1 | Bouton | Bouton pour un trade d’Achat |
ActP_sell_button1 | Bouton | Bouton pour un trade de Vente |
ActP_DealLines_check1 | Drapeau | Activer/réinitialiser le drapeau des lignes auxiliaires |
ActP_Exe_radio1 | Bouton radio | Groupe de boutons radio pour sélectionner le type de trade |
ActP_SL_edit1 | Champ de saisie | Champ de saisie d'un Stop Loss |
ActP_TP_edit1 | Champ de saisie | Champ de saisie d'un Take Profit |
ActP_Lots_edit1 | Champ de saisie | Champ de saisie du montant |
ActP_dev_edit1 | Champ de saisie | Champ de saisie d'un écart tolérable lors de l'ouverture |
ActP_mag_edit1 | Champ de saisie | Champ de saisie d'un numéro |
ActP_comm_edit1 | Champ de saisie | Classé pour la saisie de commentaires |
Tableau 1 Liste des éléments du panneau, « Ouverture du trade »
2.2 Placement d’ une Commande en Cours
Ci-dessous, nous présenterons tous les paramètres nécessaires pour passer une commande en cours et les passerons en appuyant sur une touche. Les lignes de support, activées en cochant un drapeau, aideront à définir le Stop Loss, le Take Profit, les niveaux de stop-limit et les délais d'expiration. La sélection du type d'exécution et du type de délai d'expiration sera effectuée à l'aide d'un groupe de boutons radio.
Nom | Type | Description |
---|---|---|
ActP_buy_button2 | Bouton | Bouton pour définir une commande d'achat |
ActP_sell_button2 | Bouton | Bouton pour définir une commande de trade |
ActP_DealLines_check2 | Drapeau | Les lignes auxiliaires set/reset flag |
ActP_lim_check2 | Drapeau | Indicateur de définition/réinitialisation de la limite d'arrêt de la commande |
ActP_Exe_radio2 | Bouton radio | Groupe de boutons radio pour sélectionner le type d'exécution de la commande |
ActP_exp_radio2 | Bouton radio | Groupe de bouton radio pour sélectionner le type d'expiration de la commande |
ActP_SL_edit2 | Champ de saisie | Champ de saisie d'un Stop Loss |
ActP_TP_edit2 | Champ de saisie | Champ de saisie d'un Take Profit |
ActP_Lots_edit2 | Champ de saisie | Champ de saisie du montant |
ActP_limpr_edit2 | Champ de saisie | Champ de saisie du prix d'une commande stop-limit |
ActP_mag_edit2 | Champ de saisie | Champ de saisie du nombre magique |
ActP_comm_edit2 | Champ de saisie | Champ pour commentaires |
ActP_exp_edit2 | Champ de saisie | Champ de saisie de l'heure d'expiration |
ActP_Pr_edit2 | Champ de saisie | Champ de saisie du prix de l'exécution de la commande |
Tableau 2 Liste des éléments du panneau "Passer les commandes en cours"
2.3. Modification / Clôture des Trades
Ci-dessous, nous présenterons tous les paramètres nécessaires à la modification et à la clôture d'un trade. Les lignes auxiliaires, activées en cochant une case, nous assisteront dans l'installation des niveaux Stop Loss et Take Profit. La sélection des métiers sera générée à partir d'une liste déroulante.
Nom | Type | Description |
---|---|---|
ActP_ord_button5 | La liste déroulante | Liste des sélections pour un trade |
ActP_mod_button4 | Bouton | Bouton de modification du trade |
ActP_del_button4 | Bouton | Bouton de clôture de trade |
ActP_DealLines_check4 | Drapeau | Drapeau de mise/remise à zéro des lignes auxiliaires |
ActP_SL_edit4 | Champ de saisie | Champ de saisie d'un Stop Loss |
ActP_TP_edit4 | Champ de saisie | Champ de saisie d'un Take Profit |
ActP_Lots_edit4 | Champ de saisie | Champ de saisie du montant |
ActP_dev_edit4 | Champ de saisie | Champ de saisie d'un écart tolérable |
ActP_mag_edit4 | Champ de saisie | Champ d'affichage du nombre magique (lecture seule) |
ActP_Pr_edit4 | Champ de saisie | Champ pour afficher le cours d'ouverture (lecture seule) |
Tableau 3. Liste des éléments du panneau "Modification / clôture du trade"
2.4. Modification / Retrait de Commandes
Ci-dessous, nous présenterons tous les paramètres nécessaires pour la modification et la suppression des commandes en cours. Les lignes de support, activées en cochant une case, aideront à l'installation des arrêts, des prises, des niveaux de limite d'arrêt et des délais d'expiration. La sélection du type de délais d'expiration sera générée à l'aide d'un groupe de boutons radio. La sélection des commandes sera générée à partir d'une liste déroulante.
Nom | Type | Description |
---|---|---|
ActP_ord_button5 | La liste déroulante | Liste pour sélectionner la commande |
ActP_mod_button3 | Bouton | Modification de la commande |
ActP_del_button3 | Bouton | Bouton de retrait de commande |
ActP_DealLines_check3 | Drapeau | Drapeau de mise/remise à zéro des lignes auxiliaires |
ActP_exp_radio3 | Bouton radio | Groupe de boutons radio pour sélectionner le type d'expiration d'une commande |
ActP_SL_edit3 | Champ de saisie | Champ de saisie d'un Stop Loss |
ActP_TP_edit3 | Champ de saisie | Champ de saisie d'une prise |
ActP_Lots_edit3 | Champ de saisie | Champ qui affiche le volume (lecture seule) |
ActP_limpr_edit3 | Champ de saisie | Champ de saisie du prix d'une commande stoplimit |
ActP_mag_edit3 | Champ de saisie | Champ qui affiche des nombres magiques (lecture seule) |
ActP_comm_edit3 | Champ de saisie | Champ pour commentaires |
ActP_exp_edit3 | Champ de saisie | Champ de saisie de l'heure d'expiration |
ActP_Pr_edit3 | Champ de saisie | Champ de saisie du prix d'exécution de la commande |
ActP_ticket_edit3 | Champ de saisie | Champ qui affiche le ticket de commande (lecture seule) |
Tableau 4. Liste des éléments du panneau "Modification / retrait de commandes"
2.5 paramètres
Ci-dessous, nous choisirons la couleur des boutons, des étiquettes et des textes dans la liste déroulante, ainsi que la configuration de différentes tailles de police.
Nom | Type | Description |
---|---|---|
ActP_col1_button6 | La liste déroulante | Liste des sélections de couleurs pour les boutons |
ActP_col2_button6 | La liste déroulante | Liste de sélection de couleurs pour les balises |
ActP_col3_button6 | La liste déroulante | Liste de sélection de couleur de texte |
ActP_font_edit6 | Champ de saisie | Champ pour indiquer la taille du texte |
Tableau 5. Liste des éléments du panneau "Paramètres"
Un bouton est également ajouté pour créer la possibilité de minimiser le panneau s'il n'est pas utilisé. Vous avez peut-être remarqué la présence d'instruments tels que les "lignes de support". Quels sont-ils et pourquoi en avons-nous besoin ? Grâce à l'utilisation de ces lignes, nous pourrons mettre en place un Stop Loss, un Take Profit, le prix de déclenchement d'une commande en cours, le prix d'une commande stop-limit (lignes horizontales), ainsi que le délai d'expiration de une commande différée (ligne verticale), en utilisant simplement la souris pour faire glisser ces lignes jusqu'au prix/délai souhaité.
Après tout, une installation visuelle est plus pratique qu'une installation textuelle (saisir manuellement les prix/temps dans le champ approprié). Aussi, ces lignes nous serviront de "grands moments" de paramètre d'une commande sélectionnée. Comme il peut y avoir beaucoup de commandes, les lignes ombrées du terminal standard, qui affichent généralement les prix, peuvent devenir très déroutantes.
3. L'Approche Générale de la Création d'Interface
Nous avons donc défini avec succès notre objectif - créer une forme d'assistant graphique au sein du trade. Pour cela, nous avons besoin de l'interface la plus conviviale. Premièrement, il doit être clair que tous les éléments de contrôle (et il y en aura beaucoup) devront être créés à l'aide d'un logiciel, et par conséquent, la position et la taille des objets doivent être précalculées.
Maintenant, imaginons que nous ayons traversé une longue période, fastidieuse et difficile, à travailler les coordonnées des objets, en veillant à ce qu'ils ne se chevauchent pas et soient clairement visibles ; et en suite, il faut ajouter un nouvel objet, et tout notre schéma doit maintenant être reconstruit !
Ceux qui sont familiers avec l'Environnement Elaboration rapide d'Applications (Delphi, C++ Builder, etc.) savent à quelle vitesse l'interface utilisateur la plus compliquée peut être créée.
Essayons de l'implémenter en utilisant MQL5. Tout d'abord, à l'aide d'une souris, nous localisons les objets de contrôle de la manière la plus appropriée, et ajustons leurs tailles. Ensuite, nous rédigeons un script simple, qui lit les propriétés de tous les objets sur le graphique et les enregistre dans un fichier, et si nécessaire, nous pourrons facilement récupérer ces propriétés et reconstruire complètement les objets sur n'importe quel graphique.
Le code du script peut ressembler à ceci :
//+------------------------------------------------------------------+ //| Component properties writer.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" #property script_show_inputs input int interfaceID=1; //input parameter - the identifier of the stored interface //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- //Open file for writing int handle=FileOpen("Active_Panel_scheme_"+IntegerToString(interfaceID)+".bin", FILE_WRITE|FILE_BIN); if(handle!=INVALID_HANDLE) { //We will go all the objects on the chart for(int i=0;i<ObjectsTotal(0);i++) { string name=ObjectName(0,i); //And write their properties in the file FileWriteString(handle,name,100); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR)); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100); FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE)); } //Close file FileClose(handle); Alert("Done!"); } } //+------------------------------------------------------------------+
Comme vous pouvez le voir, le code est extrêmement simple, il rédige certaines propriétés de tous les objets du graphique dans un fichier binaire. Le plus important est de ne pas oublier la commande de séquence des propriétés enregistrées lors de la lecture du fichier.
Le script est prêt, passons donc à la création de l'interface.
Et ce que nous allons faire en premier est d'organiser le menu principal par type de ses onglets. Pourquoi avons-nous besoin d'onglets ? Parce qu'il y a beaucoup d'objets, et qu'il serait problématique de les ajuster tous sur l'écran. Et puisque les objets sont regroupés en conséquence (voir tableau ci-dessus), il est plus facile de placer chaque groupe sur un onglet séparé.
Ainsi, en utilisant le menu du terminal Insertion -> Objets -> Bouton, nous allons créer cinq boutons en haut du graphique, qui serviront de menu principal.
Fig. 1 Onglets du panneau
N'oublions pas que les objets peuvent être facilement dupliqués, en sélectionnant un, puis en le faisant glisser avec une souris, tout en maintenant la touche "Ctrl" enfoncée. En faisant cela, nous créerons une copie de l'objet plutôt que de déplacer son original.
Une attention particulière doit être portée aux noms des objets, sans oublier qu'ils doivent tous commencer par "ActP". De plus, nous ajoutons "main" au nom de la chaîne, ce qui indique que l'objet appartient à la barre de menu principale.
Figure 2. Liste des objets (onglets du panneau)
De même, appliquons le contenu de l'onglet au nouveau graphique. Le contenu de chaque onglet doit être placé sur un graphique séparé !
Onglet "Maché":
.
Figure 3. Éléments de l'onglet "Marché"
Figure 4. Éléments de l'onglet "en cours"
Onglet Paramètres :
Figure 5. Éléments de l'onglet "Paramètres"
Le dernier onglet "Modifier/Clôturer" est différent, il servira à modifier/supprimer les commandes en cours, ainsi qu'à modifier et clôturer des transactions commerciales. Il sera raisonnable de diviser le travail avec les ttrades et le travail avec les commandes en deux sous-onglets distincts. Tout d'abord, créons un bouton qui activera la liste déroulante, à partir de laquelle nous choisirons une commande ou un trade avec lequel travailler.
Figure 6. Éléments de la Onglet "Modifier/clôturer""
Ensuite, nous créons des sous-onglets. Pour travailler avec les trades :
Figure 7. Éléments pour travailler avec des positions
Et pour travailler avec les commandes :
Figure 8. Sous-onglet pour travailler avec les commandes
Ça y est, l'interface est créée.
Nous appliquons le script à chacun des graphiques pour enregistrer chaque onglet dans un fichier séparé. Le paramètre d'entrée "interfaceID" doit être différent pour chaque onglet :
- 0 - Page d'accueil
- Marché
- en cours
- 3 - Bouton d'activation de la liste de sélection de trade/commande
- Paramètres
- 6 - Sous-onglet pour travailler avec les trades
- 7 - Sous-onglet pour travailler avec les commandes
L'onglet numéro 5 correspond au bouton "Réduire la fenêtre" dans le menu principal, il n'y a donc aucun objet dessus, et nous pouvons l'ignorer.
Après toutes ces manipulations, les fichiers suivants s’afficheront dans le dossier répertoire du terminal -> MQL5 -> :
Figure 9. Liste des fichiers avec des schémas de panneaux
4. Téléchargement d' Éléments d'Interface
Maintenant, les éléments de l'interface sont stockés dans des fichiers et sont prêts à être mis au travail. Pour commencer, déterminons l'endroit où sera situé notre panneau. Si on le localise directement sur le graphique principal, il bloquera le graphique des prix, ce qui est très gênant. Par conséquent, il sera plus raisonnable de placer le panneau dans la sous-fenêtre du graphique principal. Un indicateur peut créer ce volet.
Créons-le :
#property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" #property indicator_separate_window //place the indicator in a separate window int OnInit() { //--- indicator buffers mapping //Set the short name of the indicator IndicatorSetString(INDICATOR_SHORTNAME, "AP"); //--- return(0); } int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { //--- //--- return value of prev_calculated for next call return(rates_total); }
Le code est très simple car la fonction principale de cet indicateur est la création de sous-fenêtres, plutôt que de faire divers calculs. La seule chose que nous allons faire est d'installer un nom "abrégé" de l'indicateur, grâce auquel nous pouvons trouver sa sous-fenêtre. Nous allons compiler et appliquer un graphique sur l'indicateur, et une fenêtre apparaîtra.
Concentrons-nous maintenant sur le panneau Expert Advisor. Nous allons créer un nouveau conseiller expert.
La fonction OnInit() comportera les opérateurs suivants :
double Bid,Ask; //variables for current prices datetime time_current; //time of last tick int wnd=-1; //index of the window with an indicator bool last_loaded=false; //flag indicating whether it's a first initialization or not //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //Start the timer at intervals of 1 second EventSetTimer(1); //Get the latest prices get_prices(); //Define the window with an indicator wnd=ChartWindowFind(0,"AP"); //If the first initialization - create interface if(!last_loaded) create_interface(); //--- return(0); }
Ici, nous lançons une minuterie (pourquoi cela sera expliqué ci-dessous), obtenons les derniers prix du marché, en utilisant ChartWindowFind, localisons la fenêtre de l'indicateur et enregistrons-le en tant que variable. Flag last_loaded - indique s'il s'agit ou non de la première initialisation de l'Expert Advisor. Ces informations seront nécessaires afin d'éviter de recharger l'interface lors d'une réinitialisation.
La fonction create_interface() se présente comme suit :
//+------------------------------------------------------------------+ //| Function of the interface creation | //+------------------------------------------------------------------+ void create_interface() { //if reset settings is selected if(Reset_Expert_Settings) { //Reset GlobalVariableDel("ActP_buttons_color"); GlobalVariableDel("ActP_label_color"); GlobalVariableDel("ActP_text_color"); GlobalVariableDel("ActP_font_size"); } //Create the main menu interface ApplyScheme(0); //Create the interface tab "Market" ApplyScheme(1); //Set all objects as unmarked Objects_Selectable("ActP",false); //redraw the chart ChartRedraw(); }
La première étape consiste à vérifier le paramètre d'entrée "reset settings", et s'il est installé, supprimez les variables globales, responsables des paramètres. L'impact de cette action sur le panneau sera décrit ci-dessous. De plus, la fonction ApplyScheme () créera une interface à partir d'un fichier.
//+------------------------------------------------------------------+ //| The function for the interface loading | //| ID - ID of the saved interface | //+------------------------------------------------------------------+ bool ApplyScheme(int ID) { string fname="Active_Panel_scheme_custom_"+IntegerToString(ID)+".bin"; //download the standard scheme if there isn't saved scheme if(!FileIsExist(fname)) fname="Active_Panel_scheme_"+IntegerToString(ID)+".bin"; //open file for reading int handle=FileOpen(fname,FILE_READ|FILE_BIN); //file opened if(handle!=INVALID_HANDLE) { //Loading all objects while(!FileIsEnding(handle)) { string obj_name=FileReadString(handle,100); int _wnd=wnd; //the auxiliary lines are in the main window if(StringFind(obj_name,"line")>=0) _wnd=0; ENUM_OBJECT obj_type=FileReadInteger(handle); //creating object ObjectCreate(0, obj_name, obj_type, _wnd, 0, 0); //and apply the properties ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_XSIZE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_YSIZE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_COLOR,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_STYLE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_WIDTH,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_BACK,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_SELECTED,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_SELECTABLE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_READONLY,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_STATE,FileReadInteger(handle)); ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,FileReadInteger(handle)); ObjectSetString(0,obj_name,OBJPROP_TEXT,FileReadString(handle,100)); ObjectSetString(0,obj_name,OBJPROP_FONT,FileReadString(handle,100)); ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,FileReadString(handle,100)); ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,FileReadString(handle,100)); ObjectSetDouble(0,obj_name,OBJPROP_PRICE,FileReadDouble(handle)); //Set color for the objects if(GlobalVariableCheck("ActP_buttons_color") && obj_type==OBJ_BUTTON) ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,GlobalVariableGet("ActP_buttons_color")); if(GlobalVariableCheck("ActP_label_color") && obj_type==OBJ_LABEL) ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_label_color")); if(GlobalVariableCheck("ActP_text_color") && (obj_type==OBJ_EDIT || obj_type==OBJ_BUTTON)) ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_text_color")); if(GlobalVariableCheck("ActP_font_size") && (obj_type==OBJ_EDIT || obj_type==OBJ_LABEL)) ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,GlobalVariableGet("ActP_font_size")); //Set global variable font size if(obj_name=="ActP_font_edit6" && GlobalVariableCheck("ActP_font_size")) ObjectSetString(0,obj_name,OBJPROP_TEXT,IntegerToString(GlobalVariableGet("ActP_font_size"))); } //Close file FileClose(handle); return(true); } return(false); }
Encore une fois, il n'y a rien de compliqué à cela. La fonction ouvrira le fichier souhaité, avec un schéma d'interface pré-enregistré et le créera dans la fenêtre que nous avons précédemment identifiée (fenêtre indicatrice). Nous sélectionnons également les couleurs des objets et les tailles de police à partir des variables globales du terminal.
La fonction Objects_Selectable () fait tous les objets, sauf des lignes auxiliaires, non marqués, afin d'activer les animations des boutons et d'éviter de supprimer accidentellement un objet nécessaire.
//+------------------------------------------------------------------+ //| Function of setting objects as unselectable | //+------------------------------------------------------------------+ void Objects_Selectable(string IDstr,bool flag) { //Check all the objects for(int i=ObjectsTotal(0);i>=0;i--) { string n=ObjectName(0,i); //If the object belongs to the panel if(StringFind(n,IDstr)>=0) { //Lines remain untouched if(!flag) if(StringFind(n,"line")>-1) continue; //Set everything unselectable except the lines ObjectSetInteger(0,n,OBJPROP_SELECTABLE,flag); } } }
Regardons maintenant la fonction OnTick(). Elle nous servira à obtenir les derniers prix du marché.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //Get the latest prices get_prices(); }
La fonction get_prices() a la forme :
//+------------------------------------------------------------------+ //| Function obtain information on tick | //+------------------------------------------------------------------+ void get_prices() { MqlTick tick; //if the tick was if(SymbolInfoTick(Symbol(),tick)) { //obtain information Bid=tick.bid; Ask=tick.ask; time_current=tick.time; } }
Et n'oubliez pas OnDeinit() :
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- //if the deinitialisation reason isn't the timeframe or symbol change if(reason!=REASON_CHARTCHANGE) { //reset initialization flag last_loaded=false; //Delete all panel objects ObjectsDeleteAll_my("ActP"); //Delete files with the saved state of the tabs FileDelete("Active_Panel_scheme_custom_1.bin"); FileDelete("Active_Panel_scheme_custom_2.bin"); FileDelete("Active_Panel_scheme_custom_3.bin"); FileDelete("Active_Panel_scheme_custom_4.bin"); FileDelete("Active_Panel_scheme_custom_5.bin"); } //otherwise set a flag else last_loaded=true; //stop the timer EventKillTimer(); }
Vérifiez d'abord la cause de la désinitialisation : si elle est due à un changement d'une plage horaire et/ou de symboles, nous ne supprimerons pas l'élément du panneau. Dans tous les autres cas, supprimez tous les éléments à l'aide de la fonction ObjectsDeleteAll_my ().
//+------------------------------------------------------------------+ //| The function deletes all panel objects | //| IDstr - object identifier | //+------------------------------------------------------------------+ void ObjectsDeleteAll_my(string IDstr) { //check all the objects for(int i=ObjectsTotal(0);i>=0;i--) { string n=ObjectName(0,i); //if the name contains the identifier - remove the object if(StringFind(n,IDstr)>=0) ObjectDelete(0,n); } }
Après compilation et exécution de l'Expert Advisor, nous obtenons le résultat suivant :
Figure 10. Exemple d'expert Travail de conseiller
Cependant, tout cela n'a que peu d'utilité jusqu'à ce que nous soyons capables de faire réagir ces objets à notre manipulation.
5. Gestionnaire d'évènement
L'interface est créée, il faut maintenant la faire fonctionner. Toutes nos actions avec des objets génèrent des événements spécifiques. La fonction OnChartEvent OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) est le mécanisme de gestion des événements .ChartEvent De tous les événements, nous nous intéressons aux suivants :
- CHARTEVENT_CLICK - cliquez sur le graphique
- CHARTEVENT_OBJECT_ENDEDIT - fini de modifier le champ de saisie
- CHARTEVENT_OBJECT_CLICK - cliquer sur l'objet graphique
Dans notre cas, le paramètre de la fonction id indique l'ID de l'événement, sparam - indique le nom de l'objet, qui génère cet événement, et tous les autres paramètres ne nous intéressent pas.
Le premier événement que nous allons explorer est le - cliquez sur le bouton du menu principal.
5.1. Gestion des Événements du Menu Principal
Rappelons que le menu principal se compose de cinq boutons. Lorsqu'on clique sur l'un d'eux, il devrait passer en mode enfoncé, nous diriger vers la bonne interface et télécharger les onglets appropriés. Ensuite, tous les autres boutons du menu devraient passer en mode non enfoncé.
//+------------------------------------------------------------------+ //| Event handlers | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //main menu button click if(sparam=="ActP_main_1") {Main_controls_click(1); ChartRedraw(); return;} //Here we execute the corresponding operators if(sparam=="ActP_main_2") {Main_controls_click(2); ChartRedraw(); return;} if(sparam=="ActP_main_3") {Main_controls_click(3); ChartRedraw(); return;} if(sparam=="ActP_main_4") {Main_controls_click(4); ChartRedraw(); return;} if(sparam=="ActP_main_5") {Main_controls_click(5); ChartRedraw(); return;} ... } ... }
S'il y a eu un clic sur le bouton de menu, alors nous avons exécuté la fonction Main_controls_click(). Redessinons le graphique à l'aide de ChartRedraw() et complétons la fonction. Nous devons terminer l'exécution car un seul objet peut être cliqué à la fois et, par conséquent, toutes les implémentations ultérieures entraîneront une perte de temps CPU.
//+------------------------------------------------------------------+ //| Tab processor | //| ID - index of clicked tab | //+------------------------------------------------------------------+ void Main_controls_click(int ID) { int loaded=ID; //we will go all tabs for(int i=1;i<6;i++) { //for all except the selected set inactive if(i!=ID) { //also remember the last active tab if(ObjectGetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE)==1) loaded=i; ObjectSetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE,0); } } //if(loaded==ID) return; //set an active state for the selected ObjectSetInteger(0,"ActP_main_"+IntegerToString(ID),OBJPROP_STATE,1); //delete the drop-down lists DeleteLists("ActP_orders_list_"); DeleteLists("ActP_color_list_"); //and set the list buttons to the unpressed state ObjectSetInteger(0,"ActP_ord_button5",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_col1_button6",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_col2_button6",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_col3_button6",OBJPROP_STATE,0); //save state of the last active tab SaveScheme(loaded); //remove old tab DeleteScheme("ActP"); //and load a new ApplyScheme(ID); //Set all objects as unselected Objects_Selectable("ActP",false); }
Nous avons été initiés aux fonctions Objects_Selectable() et ApplyScheme(), et nous nous tournerons plus tard vers la fonction DeleteLists().
La fonction SaveScheme() enregistre un fichier d'interface, afin que les objets conservent toutes leurs propriétés lors d'un rechargement :
//+------------------------------------------------------------------+ //| Interface saving function | //+------------------------------------------------------------------+ void SaveScheme(int interfaceID) { //open file for writing int handle=FileOpen("Active_Panel_scheme_custom_"+IntegerToString(interfaceID)+".bin",FILE_WRITE|FILE_BIN); //if file opened if(handle!=INVALID_HANDLE) { //go all the chart objects for(int i=0;i<ObjectsTotal(0);i++) { string name=ObjectName(0,i); //if the object belongs to the panel if(StringFind(name,"ActP")<0) continue; //and it isn't a tab if(StringFind(name,"main")>=0) continue; //write the object properties to a file FileWriteString(handle,name,100); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE)); FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR)); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100); FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100); FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE)); } //Close file FileClose(handle); } }
La fonction DeleteScheme() supprime les objets onglet.
//+------------------------------------------------------------------+ //| Function to delete all the panel objects, except tabs | //+------------------------------------------------------------------+ void DeleteScheme(string IDstr) { //we will go through all the objects for(int i=ObjectsTotal(0);i>=0;i--) { string n=ObjectName(0,i); //remove everything but the tab if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n); } }
Ainsi, en exécutant la fonction Main_controls_click(), nous supprimerons l'ancien onglet, en l'enregistrant au préalable, et en chargerons un nouveau.
En compilant l'Expert Advisor, nous verrons les résultats.
Maintenant, nous allons cliquer sur le bouton du menu principal, charger les nouveaux onglets en les gardant dans l'état des onglets d'origine.
Figure 11. Éléments de l'onglet "en cours"
Figure 12. Eléments de l'onglet "Modifier/clôturer"
Figure 13. Éléments de l'onglet "Paramètres"
Avec cela, nous pouvons terminer la manipulation du menu principal, car désormais il remplit pleinement ses fonctions.
5.2. Gestion de l'événement de composant « Flag »
Le paramétrage des lignes auxiliaires et des commandes stoplimit se fait à l'aide des composants "flag", mais il n'est pas dans la liste des objets graphiques de MT5. Alors créons-le. Il y a un objet "étiquette graphique", qui est en fait une image qui a un état "on" et un état "off". L'état peut être modifié en cliquant sur l'objet. Une image distincte peut être définie pour chaque état. Choisissez une image pour chacun des états :
- Activé
- Désactivé
Définissons les images dans les propriétés de l'objet :
Figure 13. Paramétrage des propriétés de l'élément "drapeau"
Il faut rappeler que pour que les images soient disponibles dans la liste, elles doivent se trouver dans le dossier "Dossier Terminal-> MQL5-> Images" et avoir l'extension ".Bmp".
Passons aux événements de traitement, qui se produisent lorsque vous cliquez sur un objet. Nous allons utiliser l'exemple du drapeau, qui est responsable du placement des lignes auxiliaires à l'ouverture du trade.
//click on the flag of the setting of auxiliary lines during transaction opening if(sparam=="ActP_DealLines_check1") { //Check the flag state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //If the flag is set if(selected) { //Retrieve the value of the stop loss and take profit from the input fields string SL_txt=ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT); string TP_txt=ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT); double val_SL, val_TP; //If the Stop field is not empty //save the value if(SL_txt!="") val_SL=StringToDouble(SL_txt); //if empty else { //Take the max. and min. prices of chart double pr_max=ChartGetDouble(0, CHART_PRICE_MAX); double pr_min=ChartGetDouble(0, CHART_PRICE_MIN); //Set the stop at the 1/3 of the chart price range val_SL=pr_min+(pr_max-pr_min)*0.33; } //Similarly processes the Take if(TP_txt!="") val_TP=StringToDouble(TP_txt); else { double pr_max=ChartGetDouble(0, CHART_PRICE_MAX); double pr_min=ChartGetDouble(0, CHART_PRICE_MIN); val_TP=pr_max-(pr_max-pr_min)*0.33; } //Move the line to new positions ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, val_SL); ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, val_TP); } //If the flag is unset else { //remove the lines ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, 0); ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, 0); } //redraw the chart ChartRedraw(); //and finish the function return; }
La même méthode est utilisée pour les drapeaux, qui sont chargés du traitement et de l'installation des lignes auxiliaires sur l'onglet clôture/modification des commandes en cours. Par conséquent, nous n'entrerons pas dans les détails à leur sujet dans cet article. Ceux qui souhaitent se familiariser avec eux peuvent utiliser le code Expert Advisor.
Le paramètre d'indicateur de commandes stoplimit sur l'onglet « En cours » dispose du gestionnaire suivant :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //Click on the orders stoplimit check box if(sparam=="ActP_limit_check2") { //Check the flag state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); if(selected) //if flag is set { //set the new color for the price edit ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, White); //enable it for the edit ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, false); //установим в поле значение текущей цены //Set the current price as the field value ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, DoubleToString(Bid, _Digits)); //if the auxiliary lines are allowed //move them if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1) ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, Bid); } //if flag is unset else { //set the field unavailable for editing ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, LavenderBlush); //set the field color ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, true); //and "empty" text ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, ""); //if the auxiliary lines are allowed //move them to the zero point if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1) ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, 0); } } ... } ... }
Nous avons, à présent, fini de travailler avec les drapeaux. Examinons l'objet suivant de notre propre production - "groupe de boutons radio".
5.3. Gestion de l'événement composant "Groupe boutons radio"
À l'aide de ce composant, nous sélectionnons le type de transaction et le type d'expiration de la commande. Tout comme dans le cas des drapeaux, nous utiliserons des balises graphiques, mais cette fois, avec de nouvelles images.
- Activé
- Désactivé
Mais ici, le problème est compliqué par la nécessité de réinitialiser tous les boutons radio, à l'exception de celui sur lequel vous cliquez, à un état inactif. Prenons l'exemple du bouton radio de type d'exécution de commande :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on radion button 1 - order execution type if(sparam=="ActP_Exe1_radio2") { //check the radio button state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //set the appropriate state ObjectSetInteger(0,sparam,OBJPROP_STATE, 1); //if it selected if(selected) { //reset the other radio buttons ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false); ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false); //redraw the chart ChartRedraw(); //finish the execution of function return; } //redraw the chart ChartRedraw(); //finish the execution of function return; } //Similarly for the radio button 2 if(sparam=="ActP_Exe2_radio2") { bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); ObjectSetInteger(0,sparam,OBJPROP_STATE, 1); if(selected) { ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false); ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false); ChartRedraw(); return; } ChartRedraw(); return; } //Similarly for the radio button 3 if(sparam=="ActP_Exe3_radio2") { bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); ObjectSetInteger(0,sparam,OBJPROP_STATE, 1); if(selected) { ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false); ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false); ChartRedraw(); return; } ChartRedraw(); return; } ... } ... }
Les boutons radio du type d'expiration de la commande ne diffèrent que par le fait que lorsque vous cliquez sur le troisième, vous devez effectuer une étape supplémentaire - vous devez définir une nouvelle date dans l'heure d'entrée de l'expiration d'une commande :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //Click on the 3rd radio button - order expiration date if(sparam=="ActP_exp3_radio2") { //checking it state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); ObjectSetInteger(0,sparam,OBJPROP_STATE, 1); //if it selected if(selected) { //reset the remained radio buttons ObjectSetInteger(0, "ActP_exp1_radio2", OBJPROP_STATE, false); ObjectSetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE, false); //set the new date to the date edit field ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, White); ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, false); ObjectSetString(0, "ActP_exp_edit2", OBJPROP_TEXT, TimeToString(time_current)); //if auxiliary lines are allowed //set the new time line if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1) ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, time_current); ChartRedraw(); return; } //if it isn't selected else { //set the edit field as not available for editing ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, LavenderBlush); ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, true); //remove the auxiliary line if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1) ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, 0); } ChartRedraw(); return; ... } ... }
Nous avons maintenant fini de travailler avec les boutons radio.
5.4. Création et gestion d'événements de listes déroulantes
Nous utiliserons la liste déroulante pour choisir les commandes/trades à modifier/fermer/supprimer et le panneau de sélection des couleurs. Commençons par la liste des trades/commandes.
La première chose qui apparaît sur l'onglet "Modification/fermeture" est un bouton intitulé "Sélectionner une commande -->", ce sera le bouton qui active la liste. Lorsque vous cliquez dessus, la liste déroulante devrait se dérouler et, une fois notre sélection effectuée, elle devrait se replier à nouveau. Examinons le gestionnaire CHARTEVENT_OBJECT_CLICK de ce bouton :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the drop-down list activate button (order select) if(sparam=="ActP_ord_button5") { //check status bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //the list is activated if(selected)// the list is selected { //delete interface DeleteScheme("ActP", true); //arrays for serving the information about the orders string info[100]; //array for the tickets int tickets[100]; //initialize it ArrayInitialize(tickets, -1); //get orders info get_ord_info(info, tickets); //create the list create_list(info, tickets); } //the list isn't active else { //delete it DeleteLists("ActP_orders_list_"); } //redraw the chart ChartRedraw(); //finish the function return; } ... } ... }
Notre objectif principal est de déterminer si les trades/commandes sont sur le marché et, le cas échéant, d'en extraire des informations et de les afficher dans la liste. La fonction get_ord_info() exécute ce rôle :
//+------------------------------------------------------------------+ //| The function for obtaining the information about orders | //+------------------------------------------------------------------+ void get_ord_info(string &info[],int &tickets[]) { //initialize the counter int cnt=0; string inf; //if there is an open position if(PositionSelect(Symbol())) { //combine all order infomation in a single line double vol=PositionGetDouble(POSITION_VOLUME); int typ=PositionGetInteger(POSITION_TYPE); if(typ==POSITION_TYPE_BUY) inf+="BUY "; if(typ==POSITION_TYPE_SELL) inf+="SELL "; inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots"; inf+=" at "+DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), Digits()); //write the results info[cnt]=inf; tickets[cnt]=0; //increment the counter cnt++; } //all orders for(int i=0;i<OrdersTotal();i++) { //get ticket int ticket=OrderGetTicket(i); //if order symbol is equal to chart symbol if(OrderGetString(ORDER_SYMBOL)==Symbol()) { //combine all order infomation in a single line inf="#"+IntegerToString(ticket)+" "; int typ=OrderGetInteger(ORDER_TYPE); double vol=OrderGetDouble(ORDER_VOLUME_CURRENT); if(typ==ORDER_TYPE_BUY_LIMIT) inf+="BUY LIMIT "; if(typ==ORDER_TYPE_SELL_LIMIT) inf+="SELL LIMIT "; if(typ==ORDER_TYPE_BUY_STOP) inf+="BUY STOP "; if(typ==ORDER_TYPE_SELL_STOP) inf+="SELL STOP "; if(typ==ORDER_TYPE_BUY_STOP_LIMIT) inf+="BUY STOP LIMIT "; if(typ==ORDER_TYPE_SELL_STOP_LIMIT) inf+="SELL STOP LIMIT "; inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots"; inf+=" at "+DoubleToString(OrderGetDouble(ORDER_PRICE_OPEN), Digits()); //write the results info[cnt]=inf; tickets[cnt]=ticket; //increment the counter cnt++; } } }
EIle combinera les informations et les tickets de commande en un bloc.
De plus, la fonction create_list() créera une liste sur la base de ces informations :
//+------------------------------------------------------------------+ //| The function creates list of positions | //| info - array for the positions | //| tickets - array for the tickets | //+------------------------------------------------------------------+ void create_list(string &info[],int &tickets[]) { //get the coordinates of the list activation button int x=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_XDISTANCE); int y=ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YDISTANCE)+ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YSIZE); //get colors color col=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_COLOR); color bgcol=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_BGCOLOR); //get window height int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd); int y_cnt=0; //proceed arrays for(int i=0;i<100;i++) { //break if end reached if(tickets[i]==-1) break; //calculate the list item coordinates int y_pos=y+y_cnt*20; //if the windiow limits are reachedl, start a new column if(y_pos+20>wnd_height) {x+=300; y_cnt=0;} y_pos=y+y_cnt*20; y_cnt++; string name="ActP_orders_list_"+IntegerToString(i)+" $"+IntegerToString(tickets[i]); //create element create_button(name,info[i],x,y_pos,300,20); //and set its properties ObjectSetInteger(0,name,OBJPROP_COLOR,col); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0); ObjectSetInteger(0,name,OBJPROP_STATE,0); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol); } }
Et enfin, les fonctions DeleteLists() suppriment les éléments de la liste :
//+------------------------------------------------------------------+ //| The function for the list deletion | //+------------------------------------------------------------------+ void DeleteLists(string IDstr) { //proceed all objects for(int i=ObjectsTotal(0);i>=0;i--) { string n=ObjectName(0,i); //delete lists if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n); } }
Alors maintenant, lorsque vous cliquez sur le bouton d'activation, une liste est créée. Nous devons le faire fonctionner, car à chaque clic sur n'importe quel élément de la liste, une action spécifique doit avoir lieu. Concrètement : le chargement d'une interface pour travailler avec une commande, et le remplissage de cette interface avec des informations sur l'commande/la transaction.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... // Event - click on a graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //Click not on an item of order selection list if(StringFind(sparam, "ActP_orders_list_")<0) { //Remove it DeleteLists("ActP_orders_list_"); //Set the activation button to "unpressed" ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0); //redraw chart ChartRedraw(); } //Click on the order selection list item else { //Set a new name for the activation button ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, ObjectGetString(0, sparam, OBJPROP_TEXT)); //Set the activation button to "unpressed" ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0); //get ticket from the list item description int ticket=StringToInteger(StringSubstr(sparam, StringFind(sparam, "$")+1)); //Load the interface SetScheme(ticket); //and delete the list DeleteLists("ActP_orders_list_"); //chart redraw ChartRedraw(); } ... } ... }
C'est là où ça se complique. Comme on ne connaît pas à l'avance la taille de la liste et les noms de ses objets, nous devrons récupérer des informations en récupérant le nom de l'élément de la liste. La fonction SetScheme() configurera l'interface appropriée - pour travailler avec une transaction ou avec une commande en cours :
//+------------------------------------------------------------------+ //| The function sets the interface depending on type: | //| position or pending order | //| t - ticket | //+------------------------------------------------------------------+ void SetScheme(int t) { //if position if(t==0) { //check for its presence if(PositionSelect(Symbol())) { //delete old scheme DeleteScheme("ActP",true); //and apply new ApplyScheme(6); //set position parameters SetPositionParams(); //the objects are unavailable for the selection Objects_Selectable("ActP",false); } } //if order if(t>0) { //check for its presence if(OrderSelect(t)) { //delete old scheme DeleteScheme("ActP",true); //and apply new ApplyScheme(7); //set order parameters SetOrderParams(t); //the objects are unavailable for the selection Objects_Selectable("ActP",false); } } }
Les fonctions SetPositionParams() et SetOrderParams() installent les propriétés requises de l'interface chargée :
//+------------------------------------------------------------------+ //| Set position parameters for the objects | //+------------------------------------------------------------------+ void SetPositionParams() { //if position is exists if(PositionSelect(Symbol())) { //get its parameters double pr=PositionGetDouble(POSITION_PRICE_OPEN); double lots=PositionGetDouble(POSITION_VOLUME); double sl=PositionGetDouble(POSITION_SL); double tp=PositionGetDouble(POSITION_TP); double mag=PositionGetInteger(POSITION_MAGIC); //and set new values to the objects ObjectSetString(0,"ActP_Pr_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(pr))); ObjectSetString(0,"ActP_lots_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(lots))); ObjectSetString(0,"ActP_SL_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(sl))); ObjectSetString(0,"ActP_TP_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(tp))); if(mag!=0) ObjectSetString(0,"ActP_mag_edit4",OBJPROP_TEXT,IntegerToString(mag)); //redraw chart ChartRedraw(); } //if there isn't position, show message else MessageBox("There isn't open position for "+Symbol()); } //+------------------------------------------------------------------+ //| Set pending order parameters for the objects | //| ticket - order ticket | //+------------------------------------------------------------------+ void SetOrderParams(int ticket) { //if order exists if(OrderSelect(ticket) && OrderGetString(ORDER_SYMBOL)==Symbol()) { //get its parameters double pr=OrderGetDouble(ORDER_PRICE_OPEN); double lots=OrderGetDouble(ORDER_VOLUME_CURRENT); double sl=OrderGetDouble(ORDER_SL); double tp=OrderGetDouble(ORDER_TP); double mag=OrderGetInteger(ORDER_MAGIC); double lim=OrderGetDouble(ORDER_PRICE_STOPLIMIT); datetime expir=OrderGetInteger(ORDER_TIME_EXPIRATION); ENUM_ORDER_TYPE type=OrderGetInteger(ORDER_TYPE); ENUM_ORDER_TYPE_TIME expir_type=OrderGetInteger(ORDER_TYPE_TIME); //of order type is stoplimit, modify the interface if(type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT) { //set new value to the order price edit ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,DoubleToString(lim,_Digits)); ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,White); //set order price available for edit ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,false); } //if order type isn't stoplimit, modify the interface else { ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,""); ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,LavenderBlush); ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,true); } //check expiration type //and set interface elements switch(expir_type) { case ORDER_TIME_GTC: { ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,1); ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0); break; } case ORDER_TIME_DAY: { ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,1); ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0); break; } case ORDER_TIME_SPECIFIED: { ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0); ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,1); //in addition, set new value to the edit ObjectSetString(0,"ActP_exp_edit3",OBJPROP_TEXT,TimeToString(expir)); break; } } //set new values for the objects ObjectSetString(0,"ActP_Pr_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(pr))); ObjectSetString(0,"ActP_lots_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(lots))); ObjectSetString(0,"ActP_SL_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(sl))); ObjectSetString(0,"ActP_TP_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(tp))); ObjectSetString(0,"ActP_ticket_edit3",OBJPROP_TEXT,IntegerToString(ticket)); if(mag!=0) ObjectSetString(0,"ActP_mag_edit3",OBJPROP_TEXT,IntegerToString(mag)); ChartRedraw(); } //if there isn't such order, show message else MessageBox("There isn't an order with ticket "+IntegerToString(ticket)+" for "+Symbol()); }
Et la touche finale - la liste doit être supprimée lorsque vous cliquez sur le graphique, en utilisant CHARTEVENT_CLICK pour cet événement :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - is click on the chart if(id==CHARTEVENT_CLICK) { //delete all lists DeleteLists("ActP_orders_list_"); DeleteLists("ActP_color_list_"); //Set the activate buttons to the unpressed state ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0); ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0); ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0); ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0); ChartRedraw(); return; } ... }
En conséquence, nous avons une belle liste déroulante :
Figure 14. Un exemple de panneau de liste déroulante "Modifier/clôturer"
Nous devons maintenant créer une liste de sélection de couleurs dans l'onglet Paramètres.
Examinons les gestionnaires des boutons d'activation :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the chart if(id==CHARTEVENT_OBJECT_CLICK) { ... //Click on the button to activate the colors drop-down list if(sparam=="ActP_col1_button6") { //check state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //the list is active if(selected)//the list is active { //creat list create_color_list(100, "ActP_col1_button6", 1); //Set the position of the remaining buttons to "unpressed" ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0); ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0); //delete other lists DeleteLists("ActP_color_list_2"); DeleteLists("ActP_color_list_3"); } //the list isn't selected else { //delete it DeleteLists("ActP_color_list_"); } //redraw chart ChartRedraw(); //finish the execution of function return; } ... } ... }
Ici, nous suivons la même méthode que pour la liste de sélection des commandes.
La fonction de créer une liste diffère :
//+------------------------------------------------------------------+ //| Function for creating the colors list | //| y_max - maximal list widthа | //| ID - list ID | //| num - interface number | //+------------------------------------------------------------------+ void create_color_list(int y_max,string ID,int num) { //Get the coordinates of the list activation button int x=ObjectGetInteger(0,ID,OBJPROP_XDISTANCE); int y=ObjectGetInteger(0, ID, OBJPROP_YDISTANCE)+ObjectGetInteger(0, ID, OBJPROP_YSIZE); //get color color col=ObjectGetInteger(0,ID,OBJPROP_COLOR); //and window width int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd); y_max+=y; int y_cnt=0; //We will go through the colors array for(int i=0;i<132;i++) { color bgcol=colors[i]; //calculate list item coordinates int y_pos=y+y_cnt*20; //if we reached the boundaries of the window, start new column if(y_pos+20>wnd_height || y_pos+20>y_max) {x+=20; y_cnt=0;} y_pos=y+y_cnt*20; y_cnt++; //create new element string name="ActP_color_list_"+IntegerToString(num)+ID+IntegerToString(i); create_button(name,"",x,y_pos,20,20); //and set its properties ObjectSetInteger(0,name,OBJPROP_COLOR,col); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0); ObjectSetInteger(0,name,OBJPROP_STATE,0); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol); } }
Avançons davantage dans le processus de clic pour l'élément de liste :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on chart if(id==CHARTEVENT_OBJECT_CLICK) { ... //click isn't on the color list button if(StringFind(sparam, "ActP_color_list_1")<0) { //delete list DeleteLists("ActP_color_list_1"); //set color list activation button to "unpressed" ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0); //redraw chart ChartRedraw(); } //click on the color list else { //get color from the list color col=ObjectGetInteger(0, sparam, OBJPROP_BGCOLOR); //set it for all the buttons SetButtonsColor(col); //set button to unpressed ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0); //delete list DeleteLists("ActP_color_list_1"); //redraw chart ChartRedraw(); } ... } ... }
La fonction SetButtonsColor() définit la couleur des boutons :
//+------------------------------------------------------------------+ //| The function sets color for all buttons | //| col - color | //+------------------------------------------------------------------+ void SetButtonsColor(color col) { //We will go through all the objects for(int i=ObjectsTotal(0);i>=0;i--) { string n=ObjectName(0,i); //If the object belongs to the panel and its has a button type //set color if(StringFind(n,"ActP")>=0 && ObjectGetInteger(0,n,OBJPROP_TYPE)==OBJ_BUTTON) ObjectSetInteger(0,n,OBJPROP_BGCOLOR,col); } //set global variable GlobalVariableSet("ActP_buttons_color",col); }
Examinons les résultats ci-dessous :
Figure 15. Réglage des couleurs de boutons
Les listes de sélection de couleurs et d'étiquettes de texte sont similaires. En conséquence, nous pouvons rendre le panneau joliment coloré en quelques clics :
Figure 16. Couleurs modifiées des panneaux, des boutons et du texte
Nous en avons maintenant terminé avec les listes. Passons aux champs de saisie.
5.5. Gestion de l'événement de champ de saisie
Un champ de saisie générera un événement CHARTEVENT_OBJECT_ENDEDIT, qui se produira à la fin de l'édition du texte dans le champ. La seule raison pour laquelle nous devons gérer cet événement est due à la configuration de lignes auxiliaires pour les prix, en rapport avec les prix dans les champs de saisie.
Prenons l'exemple d'une ligne d'arrêt :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //End edit event if(id==CHARTEVENT_OBJECT_ENDEDIT)//end edit event { ... //if edit field is SL field if(sparam=="ActP_SL_edit1") { //and auxiliary lines are enabled if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1) { //get text from the field double sl_val=StringToDouble(ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT)); //move lines at new position ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, sl_val); } //redraw chart ChartRedraw(); //it ins't necessary to proceed the other objects, because the event from the one return; } ... } ... }
Les autres champs de saisie sont traités de la même manière.
5.6 Gestion des Evénements de Temporisation
Le temporisateur est utilisé pour surveiller les lignes auxiliaires. De cette façon, lorsque vous déplacez les lignes, les valeurs des prix auxquels elles sont liées, se déplacent automatiquement dans le champ de saisie. A chaque tick du minuteur, la fonction OnTimer() est exécutée.
Prenons l'exemple du placement des lignes Stop Loss et Take Profit avec l'onglet "Marché" actif :
void OnTimer()// Timer handler { //panel 1 is active if(ObjectGetInteger(0, "ActP_main_1", OBJPROP_STATE)==1) { //if auxiliary lines are allowed if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1) { //set new values to the edit fields double sl_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_SL_line1", OBJPROP_PRICE), _Digits); //stop loss ObjectSetString(0, "ActP_SL_edit1", OBJPROP_TEXT, DoubleToString(sl_pr, _Digits)); //take profit double tp_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_TP_line1", OBJPROP_PRICE), _Digits); ObjectSetString(0, "ActP_TP_edit1", OBJPROP_TEXT, DoubleToString(tp_pr, _Digits)); } } ... //redraw chart ChartRedraw(); } //+------------------------------------------------------------------+
Le suivi d'autres lignes est implémenté de la même manière.
6. Effectuer des opérations de trade
À ce stade, nous avons donc rempli tous les champs de saisie, cases à cocher, lignes et boutons radio requis. Il est maintenant temps d'essayer un peu de trading sur la base de toutes les données dont nous disposons.
6.1. Deal d’ouverture
L'onglet "Du marché" comporte les boutons "Acheter" et "Vendre". Si tous les champs sont remplis correctement, un trade devrait être mis en œuvre lorsque nous cliquons sur l'un des boutons.
Examinons les gestionnaires de ces boutons :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the object on the chart if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the Buy button if(sparam=="ActP_buy_button1") { //check its state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //if it "pressed" if(selected) { //try to perform a deal deal(ORDER_TYPE_BUY); //and set the button to the unpressed state ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //and finish the function execution return; } //****************************************** //the similar for the sell button if(sparam=="ActP_sell_button1") { bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); if(selected) { deal(ORDER_TYPE_SELL); ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } ChartRedraw(); return; } ... } ... }
Vous voyez, la fonction deal() est opérationnelle.
//+------------------------------------------------------------------+ //| Deal function | //+------------------------------------------------------------------+ int deal(ENUM_ORDER_TYPE typ) { //get the data from the objects double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit1",OBJPROP_TEXT)); double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT)); double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit1",OBJPROP_TEXT)); int mag=StringToInteger(ObjectGetString(0, "ActP_Magic_edit1", OBJPROP_TEXT)); int dev=StringToInteger(ObjectGetString(0, "ActP_Dev_edit1", OBJPROP_TEXT)); string comm=ObjectGetString(0,"ActP_Comm_edit1",OBJPROP_TEXT); ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK; if(ObjectGetInteger(0,"ActP_Exe2_radio1",OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC; //prepare request MqlTradeRequest req; MqlTradeResult res; req.action=TRADE_ACTION_DEAL; req.symbol=Symbol(); req.volume=lots; req.price=Ask; req.sl=NormalizeDouble(SL, Digits()); req.tp=NormalizeDouble(TP, Digits()); req.deviation=dev; req.type=typ; req.type_filling=filling; req.magic=mag; req.comment=comm; //send order OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Rien de surnaturel. Nous lisons d'abord les informations requises des objets et, sur leur base, créons une demande de trade.
Vérifions notre travail :
Figure 17. Opérations de trading - le résultat de l'exécution d'achat du trade
Comme vous pouvez le voir, l’achat du trade est réalisé avec succès.
6.2. Définir une Commande en Cours
Les boutons "Acheter" et "Vendre" de l'onglet "en cours" sont responsables de la passation des commandes en cours.
Examinons les gestionnaires :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the chart object if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the pending order set button if(sparam=="ActP_buy_button2") { //check the button state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //if it pressed if(selected) { ENUM_ORDER_TYPE typ; //get the pending order from the edit double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits()); //if it isn't stoplimit order if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0) { //if the order price is below the current price, set limit order if(Ask>pr) typ=ORDER_TYPE_BUY_LIMIT; //overwise - stop order else typ=ORDER_TYPE_BUY_STOP; } //if stoplimit order is specified else { //set operation type typ=ORDER_TYPE_BUY_STOP_LIMIT; } //try to place order order(typ); //set button to the unpressed state ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //and finish the execution of function return; } //****************************************** //similar for the sell pending order if(sparam=="ActP_sell_button2") { bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); if(selected) { ENUM_ORDER_TYPE typ; double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits()); if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0) { if(Bid<pr) typ=ORDER_TYPE_SELL_LIMIT; else typ=ORDER_TYPE_SELL_STOP; } else { typ=ORDER_TYPE_SELL_STOP_LIMIT; } order(typ); ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } ChartRedraw(); return; } ... } ... }
Ici, nous déterminons le type de commandes futurs sur la base de la relation entre le prix du marché actuel et le prix fixé, après quoi la fonction order() détermine la commande :
//+------------------------------------------------------------------+ //| The function places an order | //+------------------------------------------------------------------+ int order(ENUM_ORDER_TYPE typ) { //get the order details from the objects double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit2",OBJPROP_TEXT)); double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit2",OBJPROP_TEXT)); double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit2", OBJPROP_TEXT)); double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit2", OBJPROP_TEXT)); double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit2",OBJPROP_TEXT)); datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit2",OBJPROP_TEXT)); int mag=StringToInteger(ObjectGetString(0,"ActP_Magic_edit2",OBJPROP_TEXT)); string comm=ObjectGetString(0,"ActP_Comm_edit2",OBJPROP_TEXT); ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK; if(ObjectGetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC; if(ObjectGetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_RETURN; ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC; if(ObjectGetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY; if(ObjectGetInteger(0, "ActP_exp3_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED; //prepare request MqlTradeRequest req; MqlTradeResult res; req.action=TRADE_ACTION_PENDING; req.symbol=Symbol(); req.volume=lots; req.price=NormalizeDouble(pr,Digits()); req.stoplimit=NormalizeDouble(stoplim,Digits()); req.sl=NormalizeDouble(SL, Digits()); req.tp=NormalizeDouble(TP, Digits()); req.type=typ; req.type_filling=filling; req.type_time=expir_type; req.expiration=expir; req.comment=comm; req.magic=mag; //place order OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Vérifions notre travail :
Figure 18. Opérations de trading - le résultat en cours de passation de commande
Acheter stoplimit est défini avec succès.
6.3. Modification de la position
Le bouton Modifier de l'onglet "Modifier/clôturer" est responsable de la modification de la position sélectionnée :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the graphic object on the chart if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the modify position button if(sparam=="ActP_mod_button4") { //check the button state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //if it pressed if(selected)//if pressed { //modify position modify_pos(); //delete the elements of the scheme DeleteScheme("ActP" ,true); //and reset it (update the interface) SetScheme(0); //set the button to the unpressed state ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //finish the execution of function return; } ... } ... }
La fonction Modify_pos() est directement responsable de la modification :
//+------------------------------------------------------------------+ //| The function modifies the position parameters | //+------------------------------------------------------------------+ int modify_pos() { if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message"); //get the details from the edit objects double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit4",OBJPROP_TEXT)); double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit4", OBJPROP_TEXT)); int dev=StringToInteger(ObjectGetString(0,"ActP_dev_edit4",OBJPROP_TEXT)); //prepare request MqlTradeRequest req; MqlTradeResult res; req.action=TRADE_ACTION_SLTP; req.symbol=Symbol(); req.sl=NormalizeDouble(SL, _Digits); req.tp=NormalizeDouble(TP, _Digits); req.deviation=dev; //send request OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Résultats
Figure 19. Opérations de trading - le résultat de la modification des propriétés du trade (TP et SL)
Les niveaux Stop Loss et Take Profit sont modifiés avec succès.
6.4. Clôture des Positions
Le bouton clôturer de l'onglet "Modifier/clôturer" est responsable de la fermeture (éventuellement partielle) de la position :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the chart object if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the close button if(sparam=="ActP_del_button4") { //check the button state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //if pressed if(selected) { //try to close position int retcode=close_pos(); //if successful if(retcode==10009) { //delete scheme elements DeleteScheme("ActP" ,true); //set the new text for the list activisation ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select order -->"); } //set button state to unpressed ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //finish the execution of function return; } ... } ... }
La fonction close_pos() est responsable de la clôture :
//+------------------------------------------------------------------+ //| Closes the position | //+------------------------------------------------------------------+ int close_pos() { if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message"); //get the position details from the objects double lots=StringToDouble(ObjectGetString(0,"ActP_lots_edit4",OBJPROP_TEXT)); if(lots>PositionGetDouble(POSITION_VOLUME)) lots=PositionGetDouble(POSITION_VOLUME); int dev=StringToInteger(ObjectGetString(0, "ActP_dev_edit4", OBJPROP_TEXT)); int mag=StringToInteger(ObjectGetString(0, "ActP_mag_edit4", OBJPROP_TEXT)); //prepare request MqlTradeRequest req; MqlTradeResult res; //the opposite deal is dependent on position type if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { req.price=Bid; req.type=ORDER_TYPE_SELL; } else { req.price=Ask; req.type=ORDER_TYPE_BUY; } req.action=TRADE_ACTION_DEAL; req.symbol=Symbol(); req.volume=lots; req.sl=0; req.tp=0; req.deviation=dev; req.type_filling=ORDER_FILLING_FOK; req.magic=mag; //send request OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Le résultat - a clôturé 1,5 lots de trois de la transaction sélectionnée :
Figure 20. Trading clôture partielle de la position
6.5. Modification d'une commande en cours
Le bouton Modifier de l'onglet "Modification/clôture" se charge de modifier la commande sélectionnée :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the chart graphic object if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the order modify button if(sparam=="ActP_mod_button3") { bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); if(selected) { //get the order ticket from the edit string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT); long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1)); //modifying an order modify_order(ticket); //update interface DeleteScheme("ActP" ,true); SetScheme(ticket); //set button to unpressed state ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //and finish the execution of function return; } ... } ... }
La fonction Modify_order() est responsable de la modification :
//+------------------------------------------------------------------+ //| The function modifies an order | //| ticket - order ticket | //+------------------------------------------------------------------+ int modify_order(int ticket) { //get the order details from the corresponding chart objects double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit3",OBJPROP_TEXT)); double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit3",OBJPROP_TEXT)); double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit3", OBJPROP_TEXT)); double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit3", OBJPROP_TEXT)); double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit3",OBJPROP_TEXT)); datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit3",OBJPROP_TEXT)); ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC; if(ObjectGetInteger(0, "ActP_exp2_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY; if(ObjectGetInteger(0, "ActP_exp3_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED; //prepare request to modify MqlTradeRequest req; MqlTradeResult res; req.action=TRADE_ACTION_MODIFY; req.order=ticket; req.volume=lots; req.price=NormalizeDouble(pr,Digits()); req.stoplimit=NormalizeDouble(stoplim,Digits()); req.sl=NormalizeDouble(SL, Digits()); req.tp=NormalizeDouble(TP, Digits()); req.type_time=expir_type; req.expiration=expir; //send request OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Voyons le résultat - une commande est modifiée avec succès :
Figure 21. Modification de commande en cours
6.6. Supprimer un commande en cours
Le bouton Supprimer de l'onglet "Modification/Clôture" se charge de supprimer la commande sélectionnée :
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ... //Event - click on the graphic object on the chart if(id==CHARTEVENT_OBJECT_CLICK) { ... //click on the order delete button if(sparam=="ActP_del_button3") { //check the button state bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE); //if pressed if(selected) { //get the ticket from the list string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT); long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1)); //try to delete order int retcode=del_order(ticket); //if successful if(retcode==10009) { //delete all objects of the scheme DeleteScheme("ActP" ,true); //set new text for the list activation button ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select an order -->"); } //set button state to unpressed ObjectSetInteger(0, sparam, OBJPROP_STATE, 0); } //redraw chart ChartRedraw(); //and finish the execution of function return; } ... } ... }
La fonction del_order() est responsable de la suppression des commandes :
//+------------------------------------------------------------------+ //| The function for pending order deletion | //| ticket - order ticket | //+------------------------------------------------------------------+ int del_order(int ticket) { //prepare request for deletion MqlTradeRequest req; MqlTradeResult res; req.action=TRADE_ACTION_REMOVE; req.order=ticket; //send request OrderSend(req,res); //show message with the result MessageBox(RetcodeDescription(res.retcode),"Message"); //return retcode return(res.retcode); }
Examinons le résultat - la commande est supprimée.
Fig. 22 Opérations de trading - suppression d'une commande en cours
Conclusion
Enfin, toutes les fonctions du panneau ont été testées et fonctionnent avec succès.
Espérons que les connaissances acquises en lisant cet article vous aideront à élaborer des panneaux de contrôle actifs, qui vous serviront d'aides irremplaçables pour travailler sur le marché.
Pour commencer avec le panneau, vous devez décompresser l'archive dans un dossier avec le terminal, puis appliquer l'indicateur APau graphique, puis lancer ensuite Active PanelExpert Advisor.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/62





- 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