English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Création d'un Expert Advisor semi-automatique interactif par glisser-déposer axé sur un risque et un ratio R/R prédéfinis

Création d'un Expert Advisor semi-automatique interactif par glisser-déposer axé sur un risque et un ratio R/R prédéfinis

MetaTrader 5Exemples | 12 janvier 2022, 14:13
356 0
investeo
investeo

Introduction

Certains traders exécutent tous leurs trades automatiquement, et certains mélangent des trades automatiques et manuels en fonction de la sortie de plusieurs indicateurs. En tant que membre de ce dernier groupe, j'avais besoin d'un outil interactif pour évaluer dynamiquement les niveaux de prix des risques et des rendements directement à partir du graphique.

Après avoir déclaré le risque maximum sur mes capitaux propres, je voulais calculer les paramètres en temps réel en fonction du niveau de stop-loss que j'avais mis sur le graphique et je devais exécuter mon trade directement à partir de l'EA en fonction des niveaux SL et TP calculés.

Cet article présentera un moyen d’implémenter un Expert Advisor semi-automatique interactif avec un risque sur actions et un ratio R/R prédéfinis. Les paramètres de risque, de R/R et de taille de lot de l'Expert Advisor peuvent être modifiés pendant l'exécution sur le panneau EA. 


1. Exigences:

Les exigences de l'EE étaient les suivantes :

  • Capacité de prédéfinir le niveau de risque au démarrage et de le modifier pendant l'exécution pour voir comment il affecte la taille de la position
  • capacité de prédéfinir le rapport risque/rendement et de le modifier pendant l'exécution
  • capacité de calculer la taille maximale du lot en temps réel pour un niveau de risque et de stop-loss donné
  • capacité de modifier la taille du lot au moment de l'exécution pour voir comment cela affecte le risque et le rendement sur actions
  • possibilité d'exécuter des commandes d'achat/vente au marché directement depuis EA
  • interface glisser-déposer pour définir le stop-loss et voir le niveau de prix du risque prédéfini au niveau de rendement 


2. Conception

En raison des exigences pour l’EA d’affichage et modification des paramètres durant le temps d’exécution, j’ai décidé que j’utiliserais les classesCChartObjectet ses descendants pour afficher GUI sur la fenêtre du graphique et gérer les tableaux d’évènements prochains pour l’interaction de l’utilisateur. Par conséquent, l'EA avait besoin d'une interface utilisateur avec des étiquettes, des boutons et des champs d'édition.

Au début, je voulais utiliser l'objet CChartObjectPanel pour regrouper d'autres objets sur le panneau, mais j'ai décidé de tenter une approche différente, j'ai conçu une classe qui comporte des étiquettes, des champs d'édition et des boutons et de l'afficher sur un fond d'image. L'image de fond de l'interface a été réalisée à l'aide du logicielGIMP. Les objets générés par MQL5 sont des champs d'édition, des étiquettes rouges mises à jour en temps réel et des boutons.

J’ai simplement mis des étiquettes d’objet sur le graphique et enregistré leur position et construit la classe CRRDialog qui gère toute les fonctions d’affichage de sortie calculée, paramètres de réception deCChartObjectEdit états de champs et bouton d’enregistrement. Le risque de couleur et les rectangles de récompense sont des objets de classe CChartObjectRectangle pointeur glissable de stop loss est un objet de classe CChartObjectBitmap.


 

Figure 1. Capture d’écran Visuel EA

Figure 1. Capture d'écran visuelle EA

 


3. Implémentation de la classe de dialogue EA

La classe CRRDialog gère toutes les interfaces utilisateur de l'EA. EIle comporte un certain nombre de variables qui sont affichées, des objets qui sont utilisés pour afficher les variables et des méthodes pour obtenir/définir les valeurs des variables et actualiser la boîte de dialogue.

J’utilise l’objet CChartObjectBmpLabel pour l’arrière plan CChartObjectEdit objets pour champs d’édition, CChartObjectLabel objets pour affichage des étiquettes et CChartObjectButtonobjets pour bouton.: 

class CRRDialog
  {
private:

   int               m_baseX;
   int               m_baseY;
   int               m_fontSize;
   
   string            m_font;
   string            m_dialogName;
   string            m_bgFileName;

   double            m_RRRatio;
   double            m_riskPercent;
   double            m_orderLots;
   double            m_SL;
   double            m_TP;
   double            m_maxAllowedLots;
   double            m_maxTicksLoss;
   double            m_orderEquityRisk;
   double            m_orderEquityReward;
   ENUM_ORDER_TYPE   m_orderType;

   CChartObjectBmpLabel m_bgDialog;

   CChartObjectEdit  m_riskRatioEdit;
   CChartObjectEdit  m_riskValueEdit;
   CChartObjectEdit  m_orderLotsEdit;

   CChartObjectLabel m_symbolNameLabel;
   CChartObjectLabel m_tickSizeLabel;
   CChartObjectLabel m_maxEquityLossLabel;
   CChartObjectLabel m_equityLabel;
   CChartObjectLabel m_profitValueLabel;
   CChartObjectLabel m_askLabel;
   CChartObjectLabel m_bidLabel;
   CChartObjectLabel m_tpLabel;
   CChartObjectLabel m_slLabel;
   CChartObjectLabel m_maxAllowedLotsLabel;
   CChartObjectLabel m_maxTicksLossLabel;
   CChartObjectLabel m_orderEquityRiskLabel;
   CChartObjectLabel m_orderEquityRewardLabel;
   CChartObjectLabel m_orderTypeLabel;

   CChartObjectButton m_switchOrderTypeButton;
   CChartObjectButton m_placeOrderButton;
   CChartObjectButton m_quitEAButton;

public:

   void              CRRDialog(); // CRRDialog constructor
   void             ~CRRDialog(); // CRRDialog destructor

   bool              CreateCRRDialog(int topX,int leftY);
   int               DeleteCRRDialog();
   void              Refresh();
   void              SetRRRatio(double RRRatio);
   void              SetRiskPercent(double riskPercent);
   double            GetRiskPercent();
   double            GetRRRRatio();
   void              SetSL(double sl);
   void              SetTP(double tp);
   double            GetSL();
   double            GetTP();
   void              SetMaxAllowedLots(double lots);
   void              SetMaxTicksLoss(double ticks);
   void              SetOrderType(ENUM_ORDER_TYPE);
   void              SwitchOrderType();
   void              ResetButtons();
   ENUM_ORDER_TYPE   GetOrderType();
   void              SetOrderLots(double orderLots);
   double            GetOrderLots();
   void              SetOrderEquityRisk(double equityRisk);
   void              SetOrderEquityReward(double equityReward);
  };

Étant donné que les méthodes de variables get/set sont simples, je me concentrerai sur les méthodes CreateCRRDialog() et Refresh(). La méthode CreateCRRDialog() initialise l'image d'arrière-plan, les étiquettes, les boutons et les champs d'édition.

Pour initialiser les étiquettes et les champs d'édition, j'utilise : Méthode Create () avec des paramètres de coordonnées pour localiser l'objet sur le graphique, méthode Font () et FontSize () pour configurer la police et méthode Description () pour mettre du texte sur l'étiquette.

Pour les boutons Les paramètres supplémentaires de la méthode Create() indiquent la taille du bouton et la méthode BackColor() indique la couleur d'arrière-plan du bouton. 

bool CRRDialog::CreateCRRDialog(int topX,int leftY)
  {
   bool isCreated=false;

   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_baseX = topX;
   m_baseY = leftY;

   m_bgDialog.Create(0, m_dialogName, 0, topX, leftY);
   m_bgDialog.BmpFileOn(m_bgFileName);

   m_symbolNameLabel.Create(0, "symbolNameLabel", 0, m_baseX + 120, m_baseY + 40);
   m_symbolNameLabel.Font("Verdana");
   m_symbolNameLabel.FontSize(8);
   m_symbolNameLabel.Description(Symbol());

   m_tickSizeLabel.Create(0, "tickSizeLabel", 0, m_baseX + 120, m_baseY + 57);
   m_tickSizeLabel.Font("Verdana");
   m_tickSizeLabel.FontSize(8);
   m_tickSizeLabel.Description(DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE), Digits()));

   m_riskRatioEdit.Create(0, "riskRatioEdit", 0, m_baseX + 120, m_baseY + 72, 35, 15);
   m_riskRatioEdit.Font("Verdana");
   m_riskRatioEdit.FontSize(8);
   m_riskRatioEdit.Description(DoubleToString(m_RRRatio, 2));

   m_riskValueEdit.Create(0, "riskValueEdit", 0, m_baseX + 120, m_baseY + 90, 35, 15);
   m_riskValueEdit.Font("Verdana");
   m_riskValueEdit.FontSize(8);
   m_riskValueEdit.Description(DoubleToString(m_riskPercent, 2));

   m_equityLabel.Create(0, "equityLabel", 0, m_baseX + 120, m_baseY + 107);
   m_equityLabel.Font("Verdana");
   m_equityLabel.FontSize(8);
   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));

   m_maxEquityLossLabel.Create(0, "maxEquityLossLabel", 0, m_baseX + 120, m_baseY + 122);
   m_maxEquityLossLabel.Font("Verdana");
   m_maxEquityLossLabel.FontSize(8);
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*m_riskPercent/100.0,2));

   m_askLabel.Create(0, "askLabel", 0, m_baseX + 120, m_baseY + 145);
   m_askLabel.Font("Verdana");
   m_askLabel.FontSize(8);
   m_askLabel.Description("");

   m_bidLabel.Create(0, "bidLabel", 0, m_baseX + 120, m_baseY + 160);
   m_bidLabel.Font("Verdana");
   m_bidLabel.FontSize(8);
   m_bidLabel.Description("");

   m_slLabel.Create(0, "slLabel", 0, m_baseX + 120, m_baseY + 176);
   m_slLabel.Font("Verdana");
   m_slLabel.FontSize(8);
   m_slLabel.Description("");

   m_tpLabel.Create(0, "tpLabel", 0, m_baseX + 120, m_baseY + 191);
   m_tpLabel.Font("Verdana");
   m_tpLabel.FontSize(8);
   m_tpLabel.Description("");

   m_maxAllowedLotsLabel.Create(0, "maxAllowedLotsLabel", 0, m_baseX + 120, m_baseY + 208);
   m_maxAllowedLotsLabel.Font("Verdana");
   m_maxAllowedLotsLabel.FontSize(8);
   m_maxAllowedLotsLabel.Description("");

   m_maxTicksLossLabel.Create(0, "maxTicksLossLabel", 0, m_baseX + 120, m_baseY + 223);
   m_maxTicksLossLabel.Font("Verdana");
   m_maxTicksLossLabel.FontSize(8);
   m_maxTicksLossLabel.Description("");

   m_orderLotsEdit.Create(0, "orderLotsEdit", 0, m_baseX + 120, m_baseY + 238, 35, 15);
   m_orderLotsEdit.Font("Verdana");
   m_orderLotsEdit.FontSize(8);
   m_orderLotsEdit.Description("");

   m_orderEquityRiskLabel.Create(0, "orderEquityRiskLabel", 0, m_baseX + 120, m_baseY + 255);
   m_orderEquityRiskLabel.Font("Verdana");
   m_orderEquityRiskLabel.FontSize(8);
   m_orderEquityRiskLabel.Description("");

   m_orderEquityRewardLabel.Create(0, "orderEquityRewardLabel", 0, m_baseX + 120, m_baseY + 270);
   m_orderEquityRewardLabel.Font("Verdana");
   m_orderEquityRewardLabel.FontSize(8);
   m_orderEquityRewardLabel.Description("");

   m_switchOrderTypeButton.Create(0, "switchOrderTypeButton", 0, m_baseX + 20, m_baseY + 314, 160, 20);
   m_switchOrderTypeButton.Font("Verdana");
   m_switchOrderTypeButton.FontSize(8);
   m_switchOrderTypeButton.BackColor(LightBlue);

   m_placeOrderButton.Create(0, "placeOrderButton", 0, m_baseX + 20, m_baseY + 334, 160, 20);
   m_placeOrderButton.Font("Verdana");
   m_placeOrderButton.FontSize(8);
   m_placeOrderButton.BackColor(LightBlue);
   m_placeOrderButton.Description("Place Market Order");

   m_quitEAButton.Create(0, "quitEAButton", 0, m_baseX + 20, m_baseY + 354, 160, 20);
   m_quitEAButton.Font("Verdana");
   m_quitEAButton.FontSize(8);
   m_quitEAButton.BackColor(LightBlue);
   m_quitEAButton.Description("Quit");

   return isCreated;
  }

La méthode Refresh() actualise la description de toutes les étiquettes et boutons avec les variables CRRDialog et les niveaux d'offre/demande actuels, les capitaux propres du compte et les valeurs de risque sur actions: 

void CRRDialog::Refresh()
  {
   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*
                                         StringToDouble(m_riskValueEdit.Description())/100.0,2));
   m_askLabel.Description(DoubleToString(current_tick.ask, Digits()));
   m_bidLabel.Description(DoubleToString(current_tick.bid, Digits()));
   m_slLabel.Description(DoubleToString(m_SL, Digits()));
   m_tpLabel.Description(DoubleToString(m_TP, Digits()));
   m_maxAllowedLotsLabel.Description(DoubleToString(m_maxAllowedLots,2));
   m_maxTicksLossLabel.Description(DoubleToString(m_maxTicksLoss,0));
   m_orderEquityRiskLabel.Description(DoubleToString(m_orderEquityRisk,2));
   m_orderEquityRewardLabel.Description(DoubleToString(m_orderEquityReward,2));

   if(m_orderType==ORDER_TYPE_BUY) m_switchOrderTypeButton.Description("Order Type: BUY");
   else if(m_orderType==ORDER_TYPE_SELL) m_switchOrderTypeButton.Description("Order Type: SELL");
  }


4. Événements de graphique

Étant donné que EA est conçu pour être interactif, il doit gérer les événements graphiques.

Les événements qui sont traités comprennent :

  • Faire glisser le pointeur S/L pointer (SL_arrow objet de CChartObjectBitmap classe)sur le graphique - cela permettra de collecter le niveau S/L et calculer le rapport de T/P sur la base de R/R
  • bouton de changement de type de commande (achat/vente)
  • en appuyant sur le bouton « passer une commande au marché »
  • édition des champs risque, R/R et lot de commande
  • Clôturer EA après avoir appuyé sur le bouton « Quitter »

Les événements gérés sont CHARTEVENT_OBJECT_CLICK pour la sélection du pointeur et les boutons, CHARTEVENT_OBJECT_DRAG pour le déplacement du pointeur S/L et CHARTEVENT_OBJECT_ENDEDIT après la mise à jour des champs d'édition par le trader.

Au début de l’implémentation de la fonctionOnChartEvent()a pris quelques pages de code, mais j’ai décidé de la diviser en plusieurs gestionnaires d’évènement, cela a converti la fonction OnChartEvent() en forme lisible par l’Homme. 

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check the event by pressing a mouse button
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      string clickedChartObject=sparam;

      if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

      if(clickedChartObject==switchOrderTypeButtonID)
        {
         EA_switchOrderType();
        };

      if(clickedChartObject==placeOrderButtonID)
        {
         EA_placeOrder();
        }

      if(clickedChartObject==quitEAButtonID) ExpertRemove();

      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_DRAG)
     {
      // BUY 
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         EA_dragBuyHandle();
        };

      // SELL
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         EA_dragSellHandle();
        };
      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      if((sparam==riskRatioEditID || sparam==riskValueEditID || sparam==orderLotsEditID) && orderPlaced==false)
        {
         EA_editParamsUpdate();
        }
     }
  }

L'implémentation des gestionnaires d'événements sera décrite plus en détail dans les sections suivantes. Il convient de noter l'astuce que j'ai utilisée pour sélectionner l'objet SL_arrow. Normalement, pour sélectionner un objet sur la carte, il faut cliquer deux fois dessus. Mais il peut être sélectionné en cliquant une fois et invoquant la méthode() sélectionnée de CChartObjectl’objet ou son descendant dans la fonction OnChartEvent()  CHARTEVENT_OBJECT_CLICKgestionnaire d’évènement:

      if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

Objet il est sélectionné ou désélectionné en fonction de son état précédent après un clic. 


5. Classe Etendue de Gestion de l'Argent basée sur CMoneyFixedRisk

Avant de pouvoir décrire les gestionnaires ChartEvent, je dois suivre un cours de gestion de l'argent.

Pour la gestion de l'argent, j'ai réutilisé la classe CMoneyFixedRisk fournie par MetaQuotes et implémenté la classe CMoneyFixedRiskExt.

Les méthodes de classe CMoneyFixedRisk d'origine renvoient les montants de lots de commandes autorisés pour un prix, un niveau de stop-loss et un risque sur actions donnés entre la taille de lot minimale et maximale autorisée par le courtier. J'ai modifié les méthodes CheckOpenLong() et CheckOpenShort() pour renvoyer une taille de lot de 0,0 si les exigences de risque ne sont pas respectées et je l'ai étendu avec quatre méthodes : GetMaxSLPossible(), CalcMaxTicksLoss(), CalcOrderEquityRisk() et CalcOrderEquityReward():

class CMoneyFixedRiskExt : public CExpertMoney
  {
public:
   //---
   virtual double    CheckOpenLong(double price,double sl);
   virtual double    CheckOpenShort(double price,double sl);
   
   double GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType);
   double CalcMaxTicksLoss();
   double CalcOrderEquityRisk(double price, double sl, double lots);
   double CalcOrderEquityReward(double price, double sl, double lots, double rrratio);
  };

La méthode GetMaxSLPossible() calcule la valeur maximale du prix stop-loss pour un risque sur actions donné et la taille d’un trade minimal autorisé.

Par exemple, si le solde du compte est de 10 000 de la devise de base du compte et que le risque est de 2%, nous pouvons mettre à risque un maximum de 200 de la devise du compte. Si la taille minimale du lot du trade est de 0,1 lot, cette méthode renvoie le niveau de prix pour la commande ORDER_TYPE_BUY ou ORDER_TYPE_SELL qui correspondra à la valeur du risque sur actions pour une position de 0,1 lot. Cela aide à estimer quel est le niveau de stop-loss maximum que nous pouvons nous permettre pour un trade de taille de lot minimum. Il s'agit d'un niveau de prix que nous ne pouvons pas franchir pour un niveau de risque sur actions donné. 

double CMoneyFixedRiskExt::GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType)
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   double orderTypeMultiplier;
   
   if(m_symbol==NULL) return(0.0);
   
   switch (orderType)
   {
   case ORDER_TYPE_SELL: orderTypeMultiplier = -1.0; break;
   case ORDER_TYPE_BUY: orderTypeMultiplier = 1.0; break;
   default: orderTypeMultiplier = 0.0;
   }
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
 
   return (price - maxTicksLoss*m_symbol.TickSize()*orderTypeMultiplier);
}

La méthode CalcMaxTickLoss() renvoie le nombre maximum de ticks que nous pouvons nous permettre de perdre pour un risque donné et la taille de lot minimale autorisée.

Au début, la perte maximale des capitaux propres est calculée en pourcentage du solde actuel, puis la perte de valeur de tick pour le changement d'un tick pour la taille de lot minimale autorisée pour un symbole donné est calculée. Ensuite, la perte maximale des capitaux propres est divisée par la perte de valeur de tick et le résultat est. en l'arrondissant à une valeur entière avec la fonction MathFloor():

double CMoneyFixedRiskExt::CalcMaxTicksLoss()
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   
   if(m_symbol==NULL) return(0.0);
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
   
   return (maxTicksLoss);
}

La méthode CalcOrderEquityRisk() renvoie le risque sur actions pour un prix, un niveau de stop loss et un montant de lots donnés. Il est calculé en multipliant la valeur de la perte de tick par le nombre de lots et le prix, puis en multipliant par la différence entre le prix actuel et le niveau stop-loss :

double CMoneyFixedRiskExt::CalcOrderEquityRisk(double price,double sl, double lots)
{
   double equityRisk;
   
   equityRisk = lots*m_symbol.TickValueLoss()*(MathAbs(price-sl)/m_symbol.TickSize()); 
   
   if (dbg) Print("calcEquityRisk: lots = " + DoubleToString(lots) +
                 " TickValueLoss = " + DoubleToString(m_symbol.TickValueLoss()) +
                 " risk = " + DoubleToString(equityRisk));
   
   return equityRisk;
}

 La méthode CalcOrderEquityReward() est analogue à la méthode CalcOrderEquityRisk() mais elle utilise TickValueProfit() au lieu de la méthode TickValueLoss() et le résultat est multiplié par le rapport risque/rendement donné :  

double CMoneyFixedRiskExt::CalcOrderEquityReward(double price,double sl, double lots, double rrratio)
{
   double equityReward; 
   equityReward = lots*m_symbol.TickValueProfit()*(MathAbs(price-sl)/m_symbol.TickSize())*rrratio; 
   
   if (dbg) Print("calcEquityReward: lots = " + DoubleToString(lots) + 
                   " TickValueProfit = " + DoubleToString(m_symbol.TickValueProfit()) +
                 " reward = " + DoubleToString(equityReward));
   return equityReward;
}

Ces méthodes sont suffisantes pour calculer les niveaux maximaux de stop-loss et renvoyer le risque et le rendement sur actions en temps réel. La méthode CalcMaxTickLoss() est utilisée pour corriger le dessin du rectangle de risque - si le trader veut placer un trade qui dépasse la limite du nombre de ticks qu'il peut se permettre de perdre, le rectangle est dessiné uniquement jusqu'au nombre maximum de ticks qu'il peut perdre.

Cela rend la vie plus facile de le voir directement sur le graphique Vous pouvez le voir en démo à la fin de l'article.


6. Implémentation des gestionnaires d'événements de graphique

Le gestionnaire EA_switchOrderType() est déclenché après la réception de l'événement CHARTEVENT_OBJECT_CLICK sur l'objet m_switchOrderTypeButton . Il bascule le type de commande entre ORDER_TYPE_BUY et ORDER_TYPE_SELL, réinitialise l'état des boutons, les variables de la boîte de dialogue et supprime les objets rectangle de risque et de rendement sur le graphique :

void EA_switchOrderType()
  {
   symbolInfo.RefreshRates();

   visualRRDialog.SwitchOrderType();
   visualRRDialog.ResetButtons();
   visualRRDialog.SetSL(0.0);
   visualRRDialog.SetTP(0.0);
   visualRRDialog.SetMaxAllowedLots(0.0);
   visualRRDialog.SetOrderLots(0.0);
   visualRRDialog.SetMaxTicksLoss(0);
   visualRRDialog.SetOrderEquityRisk(0.0);
   visualRRDialog.SetOrderEquityReward(0.0);

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
   SL_arrow.SetInteger(OBJPROP_TIME,0,TimeCurrent());

   rectReward.Delete();
   rectRisk.Delete();

   visualRRDialog.Refresh();

  }

Le gestionnaire EA_dragBuyHandle() est déclenché après que l'objet SL_arrow ait été glissé et déposé sur le graphique. Au début, il lit le temps de point de chute de l'objet SL_arrow et les paramètres de prix du graphique et définit le niveau de prix comme un stop-loss hypothétique pour notre trade.

Ensuite, il calcule combien de lots pouvons-nous ouvrir pour un risque donné sur les capitaux propres. Si la valeur stop loss ne peut pas garantir l'objectif de risque pour le lot de trading le plus bas possible sur ce symbole, elle est automatiquement déplacée vers le niveau SL maximum possible. Cela permet d'évaluer l'espace dont nous disposons pour le stop loss pour un risque donné.

Après avoir calculé le risque et le rendement, les objets rectangle sont mis à jour sur le graphique.

void EA_dragBuyHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

// BUY
   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);

   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()<SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), 
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_dragSellHandle() est déclenché pour la configuration de la commande de vente.

Les calculs sont basés sur le prix et les rectanglessymbolInfo.Bid() sont dessinés en conséquence, c'est-à-dire que le profit de marquage de la zone verte est inférieur au niveau de prix actuel. 

void EA_dragSellHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double maxSLAllowed=MM.GetMaxSLPossible(symbolInfo.Bid(),ORDER_TYPE_SELL);

   if(SL_price>maxSLAllowed)
     {
      SL_price=maxSLAllowed;
      SL_arrow.SetDouble(OBJPROP_PRICE,0,maxSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()>SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Bid(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(),
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_placeOrder() est déclenché après avoir appuyé sur l'objet m_placeOrderButton. Il place une commande d'achat ou de vente au marché pour des niveaux SL et TP calculés et une taille de lot donnée.

Veuillez noter à quel point il est facile de passer un ordre au marché en utilisant la classe CExpertTrade. 

bool EA_placeOrder()
  {
   symbolInfo.RefreshRates();
   visualRRDialog.ResetButtons();

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
      orderPlaced=trade.Buy(visualRRDialog.GetOrderLots(),symbolInfo.Ask(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
      orderPlaced=trade.Sell(visualRRDialog.GetOrderLots(),symbolInfo.Bid(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));

   return orderPlaced;
  }

Le gestionnaire EA_editParamsUpdate() est déclenché lorsque la touche Entrée est enfoncée après avoir modifié l'un des champs d'édition : riskRatioEdit, riskValueEdit et orderLotsEdit.

Lorsque cela se produit, la taille du lot autorisé, le niveau de TP, la perte maximale de tick, le risque sur actions lié et le rendement doivent être recalculés :

void EA_editParamsUpdate()
  {
   MM.Percent(visualRRDialog.GetRiskPercent());

   SL_arrow.GetDouble(OBJPROP_PRICE, 0, SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME, 0, startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);
   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                     SL_price, visualRRDialog.GetOrderLots()));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                       visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();
   rectUpdate(visualRRDialog.GetOrderType());

   ChartRedraw();
  }

EA_onTick() est invoqué à chaque fois qu'un nouveau tick arrive. Les calculs ne sont effectués que si la commande n'a encore pas été passée et que le niveau de stop loss a déjà été choisi en faisant glisser le pointeur SL_arrow.

Une fois la commande passée, le risque et la rendement et le niveau de TP ainsi que le réexamen du risque et de le rendement ne sont pas nécessaires. 

void EA_onTick()
  {
   if(SL_price!=0.0 && orderPlaced==false)
     {
      double lotSize=0.0;
      SL_price=visualRRDialog.GetSL();
      symbolInfo.RefreshRates();

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
         lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
         lotSize=MM.CheckOpenShort(symbolInfo.Ask(),SL_price);

      visualRRDialog.SetMaxAllowedLots(lotSize);
      if(visualRRDialog.GetOrderLots()>lotSize) visualRRDialog.SetOrderLots(lotSize);

      visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                           SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(
                                           symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      visualRRDialog.Refresh();
      rectUpdate(visualRRDialog.GetOrderType());
     }

   ChartRedraw(0);
  }

La fonction rectUpdate() est chargée de redessiner les rectangles de couleur en rapport avec le risque et le rendement Les points de contrôle sont l'heure de début de l'objet SL_arrow, la valeur actuelle du prix Ask ou Bid selon le type de commande et les niveaux SL et TP. Le rectangle rose clair indique la fourchette de prix entre le prix actuel et le niveau SL et le rectangle vert clair indique la fourchette de prix entre le prix actuel et le niveau TP.

Les deux rectangles sont un bon outil pour observer l'impact du rapport risque/rendement sur les niveaux de prix SL et TP et aident à ajuster le risque avant d'entrer dans le commerce.

void rectUpdate(ENUM_ORDER_TYPE orderType)
  {
   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   if(orderType==ORDER_TYPE_BUY)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Ask(),currentTime,symbolInfo.Ask()+
                       (symbolInfo.Ask()-visualRRDialog.GetSL())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Ask());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
   else if(orderType==ORDER_TYPE_SELL)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Bid(),currentTime,symbolInfo.Bid()-
                        (visualRRDialog.GetSL()-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Bid());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
  }

 

7. Demo

Veuillez observer ci-dessous la démonstration de l'Expert Advisor en action. Je fais une commande de vente après un gros rebond peu après l'ouverture du marché le lundi 01/11/2010.

Pour une meilleure expérience de visionnage, veuillez régler la vidéo en plein écran et la qualité à 480p. Les commentaires sont compris dans la vidéo :

 

Conclusion

Dans l'article suivant, j'ai présenté un moyen de créer un Expert Advisor interactif pour le trading manuel basé sur un rapport risque/rendement prédéfini.

J'ai expliqué comment utiliser des classes standard pour afficher du contenu sur le graphique et comment gérer les événements du graphique pour saisir de nouvelles données et gérer les objets par glisser-déposer. J'espère que les idées que j'ai présentées serviront de base à la construction d'autres outils visuels configurables en MQL5.

Tous les fichiers sources et bitmaps sont joints à l'article.

Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/192

Fichiers joints |
visualrrea.mq5 (13.84 KB)
crrdialog.mqh (13.95 KB)
visualrrids.mqh (0.8 KB)
images.zip (159.05 KB)
Concevoir et implémenter de nouveaux widgets GUI axés sur la classe CChartObject Concevoir et implémenter de nouveaux widgets GUI axés sur la classe CChartObject
Après avoir écrit un article précédent sur l'Expert Advisor semi-automatique avec interface graphique, il s'est avéré qu'il serait souhaitable d'améliorer l'interface avec de nouvelles fonctionnalités pour des indicateurs et des Expert Advisors plus complexes. Après m'être familiarisé avec les classes de bibliothèque standard MQL5, j'ai implémenté de nouveaux widgets. Cet article décrit un processus de conception et d’implémentation de nouveaux widgets d'interface graphique MQL5 pouvant être utilisés dans des indicateurs et des Expert Advisors. Les widgets présentés dans l'article sont CChartObjectSpinner, CChartObjectProgressBar et CChartObjectEditTable.
Un démarrage rapide ou un petit guide pour les débutants Un démarrage rapide ou un petit guide pour les débutants
Bonjour cher lecteur ! Dans cet article, je vais essayer de vous expliquer et de vous montrer comment maîtriser facilement et rapidement les principes de création d'Expert Advisors, de travail avec des indicateurs, etc. Il est destiné aux débutants et ne comportera pas d'exemples difficiles ou abstraits.
Calculs Parallèles dans MetaTrader 5 Calculs Parallèles dans MetaTrader 5
Le temps a été une grande valeur tout au long de l'histoire de l'humanité, et nous nous efforçons de ne pas le gaspiller inutilement. Cet article vous indiquera comment accélérer le travail de votre Expert Advisor si votre ordinateur dispose d'un processeur multi-noyau. De plus, l’implémentation de la méthode proposée ne nécessite la connaissance d'aucun autre langage que MQL5.
Stratégies d’ordres. Un Expert Advisor polyvalent Stratégies d’ordres. Un Expert Advisor polyvalent
Cet article se concentre sur les stratégies qui utilisent activement les ordres en attente, un métalangage qui peut être créé pour décrire formellement de telles stratégies et l'utilisation d'un Expert Advisor polyvalent dont le fonctionnement est basé sur ces descriptions.