Comment créer des objets de manière dynamique ? (Quelques trucs de POO)

 

Voici quelques trucs OOP.

L'idée du programme :

* Je dessine une ligne de tendance sur le graphique et je la nomme "beep" - la prochaine fois que le prix franchira cette ligne, je recevrai un bip.

* Je dessine une ligne de tendance et je la nomme "buy" - la prochaine fois que le prix franchira cette ligne, j'obtiendrai une position longue.

J'ai déjà écrit un objet nommé "CSmartLine" qui est capable d'émettre un signal sonore, d'acheter, de vendre, de fermer et ... (pas de service de café jusqu'à présent).

Dans mon EA, j'ai trois lignes de code :

CSmartLinie mSmartLinie1;    // Create one object of class CSmartLine  


void OnTick()
      mSmartLinie1.CheckForAction(); // check for crossing prices

void OnChartEvent()
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         if (sparam == "beep" || sparam == "buy" || sparam == "sell" || sparam == "close")
            {
            mSmartLinie1.CheckForAction(sparam);  // activation and tracking changes
            return;
            };

Cela fonctionne bien jusqu'à présent.

Maintenant . . . Je voudrais dessiner un nombre quelconque de lignes de tendance intelligentes sur le graphique.

En supposant que l'objet change le nom de la ligne (par exemple en "SmartLine_x") afin de montrer que la ligne est sous son contrôle.

Chaque fois que l'EA détecte une nouvelle ligne, il devrait créer un nouvel objet de la classe "CSmartLine".

Le code pourrait être

OnChartEvent()
      if (sparam = "beep")
             mSmartLine2 = new CSmartLine;

OnTick()
      if(mSmartLine2 != Null)
             mSmartLine2.CheckForAction();

Mais comment .... hmmm ....

Peut-être que "mSmartLine" devrait être un tableau de pointeurs ? Si oui, de quel type ?

Le manuel montre à cet endroit un exemple que je n'arrive pas à comprendre.


Lorsque la ligne de tendance disparaît (parce que l'utilisateur l'a supprimée du graphique par exemple) .

Le code devrait être . . .

      delete mSmartLine2;
      mSmartLine2=NULL;

Parce que c'est l'objet lui-même qui reconnaît la disparition de la ligne, ce devrait être l'objet qui se supprime lui-même, plutôt que de demander à l'EA de le supprimer.

WIllbur

 

Où l'on peut simplement utiliser

ObjectCreate(0,"mSmartLinie"+IntegerToString(X),OBJ_HLINE,0,0,0);

Où un simple

X++;

augmentera l'entier X de manière à créer

"mSmartLinie0"
"mSmartLinie1"
"mSmartLinie2"
"mSmartLinie3"

Et etc.

 
Marco a raison.

Votre problème est que si un objet "beep" existe, un nouvel objet ne sera pas créé.
Comme marco l'a mentionné
plus
Inclure les commandes sur le premier caractère du nom de l'objet après une
séquence de caractères reconnaissable

ex :

A->Bip
B->Acheter
C->Vendre
séquence -> SMRTLN_

donc la première occurrence d'un bip smartline est nommée "SMRTLN_A_"+TotalBeepCount
 

Dans la POO, il existe des modèles de conception communs pour le stockage des objets et leur itération. Ils sont appelés Containers, Sets, Collections, Maps, Vectors (et d'autres noms similaires), mais à ce jour aucun n'est livré avec la distribution de base de MQL. Vous devez les coder vous-même ou les trouver dans la base de code.

C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
  • en.wikibooks.org
A design pattern is neither a static solution, nor is it an algorithm. A pattern is a way to describe and address by name (mostly a simplistic description of its goal), a repeatable solution or approach to a common design problem, that is, a common way to solve a generic problem (how generic or complex, depends on how restricted the target goal...
 

Je pense que j'ai compris - ce sont les lignes magiques (j'espère) :

CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object


On ChartEvent

           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make the new object the owner of the new trend line
           mSmartLine.SetName(sparam);
           //--- Place the pointer value in an Array
           ArrayOfSmartLineS[NoLines]=mSmartLine;

Dans le manuel on utilise la forme "CSmartLine*mSmartLine = new CSmartLine() ;" seulement quand on crée la première instance de cette classe.
La fois suivante, c'est seulement "mSmartLine = new CSmartLine() ;".
Ce n'est pas possible dans mon cadre de test (erreurs de compilation). ? ? ?


J'ai écrit un simple cadre de test avec une fonctionnalité très pauvre mais le test pour la création dynamique d'objets.

Chaque fois que l'EA détecte une ligne de tendance nommée "beep" - il crée un objet "SmartLine" qui renomme la ligne et la contrôle dorénavant.

//+------------------------------------------------------------------+
//| Test frame OOP                                                   |
//+------------------------------------------------------------------+
class CSmartLine
{
protected:
string   iName;
double   iRate;

public:
   void   SetName(string xName)
            {
            for(int i=0;i<99;i++)
               {
               iName = "SmartLine_"+IntegerToString(i);
               if(ObjectFind(0,iName) < 0) break; // find unused name
               }
            ObjectSetString(0,xName,OBJPROP_NAME,0,iName); // rename trend line
            // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            // signal identification of the line
               Sleep(300); PlaySound("ok.wav");
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,4); ChartRedraw();
               Sleep(300);
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,1); ChartRedraw();
            //
            };
           
   string GetName(void) {return(iName);}
  
   void   checkForChange(string xName)
            {
            if(xName != iName) return;
            // Check whether the line has been moved
            // get the new position
                        // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            MessageBox("New rate: "+iName+" = "+DoubleToString(iRate,5));
            };
   void   checkForAction(double iAsk)
            {
            if(MathAbs(100 * (iRate - iAsk)/iAsk) < 0.005)
              {
              MessageBox("it's hit me "+iName+
              "\n myRate "+DoubleToString(iRate,5)+
              "\n actAsk "+DoubleToString(iAsk, 5)+
              "\n actDiff "+DoubleToString(100 * (iRate - iAsk)/iAsk,5) );
              }
            // Calculation whether the price hits the line
            // action: beep, buy, sell, close
            };         

};

//################# E N D - - - C S m a r t L i n e ##########################

//################# B E G I N of E A program ##################################

//--- Declare an array of object pointers of type CSmartLine

      CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object

int   NoLines=0;

//----------------------------------------------------------------------------
void OnInit(void)
{
// --- do I need this?
   for(int i=0;i<10;i++)
     ArrayOfSmartLineS[i]=NULL;     

//--- delete all old trend lines
    ObjectsDeleteAll(0,"SmartLine",-1);

}
//+--------------------------------------------------------------------------
void OnChartEvent(const int id,        
                  const long& lparam,  
                  const double& dparam,
                  const string& sparam) 
{
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         {
         if(sparam == "beep" || sparam == "buy" || sparam == "sell") 
           {
           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make to new object the owner of the new line
           mSmartLine.SetName(sparam);
           //--- file the pointer value in the array[0]
           ArrayOfSmartLineS[NoLines]=mSmartLine;
           //--- ask the new object for it's line name
           MessageBox("new object: " + ArrayOfSmartLineS[NoLines].GetName());
           //
           NoLines++;
           };
         if(StringSubstr(sparam,0,10) == "SmartLine_")
           {
           for(int i=0;i<10;i++)  // Ask all exsisting objects to pick up the change if concerns
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForChange(sparam);
             }
           }

         }
}
//----------------------------------------------------------------------------
void OnTick(void)
{
      MqlTick last_tick;
  
      SymbolInfoTick(_Symbol,last_tick);
     
      for(int i=0;i<10;i++)  // Ask all exsisting objects
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForAction(last_tick.ask);
             }
}
//+------------------------------------------------------------------+
void OnDeinit(const int xReason)
{
      if(xReason == REASON_RECOMPILE   ||
         xReason == REASON_CHARTCHANGE ||
         xReason == REASON_PARAMETERS  ||
         xReason == REASON_ACCOUNT)    return;
     
//--- We must delete all created dynamic objects
      for(int i=0;i<10;i++)
         {
         //--- We can delete only the objects with pointers of POINTER_DYNAMIC type
         if(CheckPointer(ArrayOfSmartLineS[i])==POINTER_DYNAMIC)
            {
            //--- Notify of deletion
            MessageBox("Deleting object "+IntegerToString(i)+" named "+ArrayOfSmartLineS[i].GetName());
            //--- Delete an object by its pointer
            delete ArrayOfSmartLineS[i];
            ArrayOfSmartLineS[i] = NULL;
            }
         }   // Loop i=0;i<10;i++
  }

 

@Marco

Je ne suis pas sûr d'avoir compris votre idée.

Vous voulez dire que je devrais utiliser les objets graphiques standard, écrire une classe qui hérite de tout ce qu'ils contiennent et augmenter la fonctionnalité jusqu'à la gamme que je prévois pour mes SmartLines ? !?

J'ai réfléchi à cette idée et je me demande si MT5 permet à l'utilisateur de créer un objet graphique dans un graphique (via l'interface normale).

En plus de ce problème, mes objets SmartLine doivent être déclenchés lorsque le prix change et je n'ai aucune idée de comment contourner ce problème.

Avez-vous de l'expérience dans ce domaine ?

Willbur

 
Willbur:

@Marco

Je ne suis pas sûr d'avoir compris votre idée.

Vous voulez dire que je devrais utiliser les objets graphiques standard, écrire une classe qui hérite de tout et augmente la fonctionnalité à la gamme que je prévois pour mes SmartLines ? !?

J'ai réfléchi à cette idée et je me demande si MT5 permet à l'utilisateur de créer un objet graphique dans un graphique (via l'interface normale).

En plus de ce problème, mes objets SmartLine doivent être déclenchés lorsque le prix change et je n'ai aucune idée de comment contourner ce problème.

Avez-vous de l'expérience dans ce domaine ?

Willbur

C'était juste un exemple pour faire les choses.

Chaque fois que je construis une GUI ou interface utilisateur graphique, je construis toujours des cadres de contrôle dans une boucle.

Cela signifie que les boutons de contrôle sont simplement créés comme B0, B1, B2, B3, B4, B5, etc., ce qui se traduit par le bouton 0, le bouton 1, le bouton 2 et ainsi de suite.

Et j'utilise toujours IntegerToString() pour ajouter le nombre entier numérique au nom de l'objet.

Si vous voulez le déclencheur, vous pouvez aussi utiliser :

if((Ask+Bid)/2>ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 1
 }

else if((Ask+Bid)/2<ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 2
 }

Ou une variante puisque c'est seulement pour vous donner quelques idées.

Donc, vous prenez un prix Ask, Bid ou Median et vous le comparez au double de votre ligne H actuellement.

La seule limite est votre propre imagination.

 

Marco, tu es toujours dans ton EA-Code. Vous l'êtes ?

Ce n'est pas ce dont je parle.

L'idée était d'ajouter des fonctionnalités à l'objet graphique donné en écrivant mon propre objet qui hérite de l'objet graphique original et l'augmente avec les fonctions "buy" et "sell".

Si vous procédez ainsi, mon objet SmartLine devrait apparaître dans le menu de MT5, à côté de la ligne de tendance, des flèches, de l'objet texte et de tous ces éléments.

Vous pouvez le sélectionner avec la souris comme les autres objets graphiques et l'ajouter au graphique.

Si MT5 permet cela, alors seulement nous devons discuter de la question restante, comment l'objet pourrait être déclenché par le programme du terminal quand le prix change.

Aucun des objets graphiques existants n'est capable de réagir aux changements de prix (pour autant que je sache).

Willbur

 

Je n'aime pas dire que vous le faites totalement mal, mais c'est le cas, car il s'agit de programmation structurée et non de POO. La grande différence est le pouvoir de l'héritage et de la surcharge. Et d'ailleurs, vous ne pouvez pas vraiment hériter d'objets graphiques réels, mais vous pouvez représenter n'importe quoi comme un objet de code et faire référence à une ligne ou autre à partir de cet objet. C'est la façon dont cela se fait habituellement dans n'importe quelle classe, que ce soit une classe MFC ou MQL, c'est la même chose.

Si vos lignes sont des objets, alors traitez-les comme tels. Ne traitez pas les tableaux à l'extérieur, faites-le dans une classe de collection et travaillez avec des pointeurs. Jetez un coup d'oeil à CWndContainer pour vous en faire une idée. Cette classe est un conteneur qui gère principalement les tableaux de pointeurs pour les objets CWnd. Allez un pas plus loin, votre structure devrait être :

CObject comme base pour chaque objet

CPriceTimeObjects comme base pour chaque objet basé sur le prix/le temps, comme les lignes, dérivé de CObject. Il contrôle la création, conserve le temps et le prix et appelle un OnCreate(), qui peut être utilisé par le prochain héritier. Il possède également une fonction Tick qui appelle un OnTick() virtuel, qui est ensuite surchargé par les héritiers.

CTrendLine comme base pour les lignes de tendance, hérite de CPriceTimeObjects et gère OnCreate où il crée la ligne finale en utilisant la fonction ObjectCreate. Il devrait également avoir un gestionnaire OnTick() pour réagir/répondre aux événements Tick, parce qu'il sera sensible au prix si j'ai bien compris.

En plus de cela, vous avez une classe conteneur qui gère un tableau de pointeurs qui contient tous les objets CTimePriceObject que vous voulez, il hérite lui-même aussi de CTimePriceObject et passe OnTick() à ses "enfants". Le conteneur possède également une fonction qui gère le OnChartEvent() pour ajouter ou supprimer des lignes. Il devrait également avoir une fonction de balayage pour analyser tous les objets existants dans le cas où l'expert a été ajouté après la création des lignes. De plus, elle gère le OnTick() surchargé de CTimePrice, boucle le tableau, demande à chaque objet CTrendLine s'il se sent responsable de réagir en appelant la fonction Tick de chaque objet enfant qui est géré par un OnTick virtuel. Pourquoi cela ? Parce que la CTrendLine surcharge cette fonction également à partir de CTimePrice et de cette façon cette classe peut également être héritée par d'autres héritiers avec d'autres fonctions.

Votre code devrait ressembler à ceci plus tard :

CTPContainer container ;

::OnChartEvent(...)

container.ChartEvent(id, lparam, dparam, sparam) //... résulte en OnCreate() et OnDelete() à chaque CTrendLineObject. Le conteneur décide de ce qu'il faut faire, pas votre EA.

::OnTick()

container.Tick() // ... résulte en OnTick() pour chaque objet CTrendLine "enfant".

et ainsi de suite.

Il s'agit d'une base OOP claire qui peut facilement être améliorée par des fonctions utiles sans jamais toucher à un EA lui-même qui utilise ces classes.

 
Je n'aime pas dire que vous le faites totalement mal, mais vous le faites... <br / translate="no">

Wow ... merci pour la leçon.

D'une certaine manière, il semble que vous avez la bonne approche.

Je vais essayer de changer mon code dans cette direction avant de revenir avec elle.

Willbur

 

Si vous vous lancez dans le codage orienté objet de manière conséquente et immédiate, vous ne le regretterez jamais. Les débuts peuvent être plus difficiles que la méthode descendante normale, mais vous serez en mesure de développer à un niveau beaucoup plus élevé qui comprend plus de puissance, plus de possibilités, plus de flexibilité et une compatibilité plus facile avec les changements futurs.

MQL est génial, mais sans la POO, vous ne découvrirez jamais ce qu'il est vraiment.

Si vous avez des questions, posez-les et j'essaierai de vous aider.