English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Création de Panneaux de Contrôle Actifs dans MQL5 pour le Trading

Création de Panneaux de Contrôle Actifs dans MQL5 pour le Trading

MetaTrader 5Expert Advisors | 16 novembre 2021, 15:40
352 0
Евгений
Евгений

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.

Figure 1. Onglets du panneau

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)

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 3. Éléments de l'onglet "Marché"

Onglet "en cours" :

Figure 4. Éléments de l'onglet « en cours »

Figure 4. Éléments de l'onglet "en cours"

Onglet Paramètres :

Figure 5. Éléments de l'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 l'onglet

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

Figure 7. Éléments pour travailler avec des positions

Et pour travailler avec les commandes :

Figure 8. Sous-onglet 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 8. Liste des fichiers de schémas de panneaux

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 de travail d'Expert Advisor

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 11. Éléments de l'onglet "en cours"

Figure 12. Éléments de l'onglet "Modifier/clôturer"

Figure 12. Eléments de l'onglet "Modifier/clôturer"


Figure 12. Éléments de l'onglet "Paramètres"

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 "flag"

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"

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. Paramétrage des couleurs des boutons

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

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

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 placement de commande

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 (définir TP et SL)

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

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 la commande en cours

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.

Figure 22. Trading - suppression d'une commande en cours

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

Fichiers joints |
Connexion de l'Expert Advisor avec ICQ en MQL5 Connexion de l'Expert Advisor avec ICQ en MQL5
Cet article décrit la méthode d'échange d'informations entre l'Expert Advisor et les utilisateurs d' ICQ, plusieurs exemples sont présentés. La documentation fournie sera intéressante pour ceux qui souhaitent recevoir des informations de trading à distance depuis un terminal client, via un client ICQ dans leur téléphone mobile ou PDA.
Création et publication des rapports de trade et de notifications par SMS Création et publication des rapports de trade et de notifications par SMS
Les traders ne sont toujours pas en mesure et n’ont pas envie de s'asseoir au terminal de trading pendant des heures. Surtout si le système trading est plus ou moins formalisé et peut identifier automatiquement certains états du marché. Cet article décrit comment générer un rapport des résultats du trade (à l'aide d'Expert Advisor, d'un indicateur ou d'un script) sous forme de fichier HTML et le télécharger via FTP sur le serveur WWW. Nous envisagerons également d'envoyer des notifications d'événements de trade sous forme de SMS sur un téléphone mobile.
Créer un jeu "Serpent" en MQL5 Créer un jeu "Serpent" en MQL5
Cet article décrit un exemple de programmation de jeu "Snake". Dans MQL5, la programmation du jeu est devenue possible principalement grâce aux fonctionnalités de gestion des événements. La programmation orientée-objet simplifie considérablement ce processus. Dans cet article, vous allez apprendre les fonctionnalités de traitement des événements, les exemples d'utilisation des classes Standard MQL5 Library et les détails des appels de fonction périodiques.
Un Exemple de Stratégie de Trading Axée sur les Différences de Fuseau Horaire sur Différents Continents Un Exemple de Stratégie de Trading Axée sur les Différences de Fuseau Horaire sur Différents Continents
En surfant sur Internet, il est facile de trouver de nombreuses stratégies, qui vous donneront un certain nombre de recommandations diverses. Adoptons une approche d'initié et examinons le processus de création d'une stratégie, axée sur les différences de fuseaux horaires sur les différents continents.