¿Cómo crear objetos de forma dinámica? (Algunas cosas de POO)

 

Aquí vienen algunas cosas OOP.

La idea del programa:

* Dibujo una línea de tendencia en el gráfico y la llamo "bip" - la próxima vez que el precio cruce esta línea, obtendré un bip

* Dibujo una línea de tendencia y la nombro "comprar" - la próxima vez que el precio cruce esta línea, obtendré una posición larga.

Escribí ya un objeto llamado "CSmartLine" que es capaz de pitar y comprar y vender y cerrar y ... (sin servicio de café hasta ahora).

En mi EA tengo tres líneas de código:

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;
            };

Hasta ahora funciona bien.

Ahora ... ... Me gustaría dibujar cualquier número de líneas de tendencia inteligentes en el gráfico.

Suponiendo que el objeto cambia el nombre de la línea (por ejemplo, a "SmartLine_x") con el fin de mostrar que la línea está bajo su control.

Cada vez que el EA detecte una nueva línea, deberá crear un nuevo objeto de la clase "CSmartLine".

El código podría ser

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

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

Pero como .... hmmm ....

Quizás "mSmartLine" debería ser un array de punteros? Si es así, ¿de qué tipo?

El manual muestra en su punto un ejemplo que no consigo entender.


Cuando la línea de tendencia desaparece (porque el usuario la ha borrado del gráfico por ejemplo) .

El código debería ser .

      delete mSmartLine2;
      mSmartLine2=NULL;

Como es el propio objeto el que reconoce la desaparición de la línea, debería ser el objeto el que se borrara a sí mismo, en lugar de pedirle al EA que lo borrara.

WIllbur

 

Donde uno puede simplemente usar

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

Donde un simple

X++;

aumentará el número entero X para crear

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

Y etc.

 
Marco tiene razón.

El problema es que si existe un objeto "bip" no se creará uno nuevo.
Como mencionó Marco
más
Incluir los comandos en el primer carácter del nombre del objeto después de una
secuencia de caracteres reconocible

Ej:

A->Beep
B->Comprar
C->Vender
secuencia -> SMRTLN_

por lo que la primera ocurrencia de una línea inteligente de pitidos se llama "SMRTLN_A_"+Cuento total de pitidos
 

En POO hay patrones de diseño comunes para almacenar objetos e iterar con ellos. Se denominan patrones de diseño Containers, Sets, Collections, Maps, Vectors (y otros nombres similares), pero al parecer ninguno se entrega con la distribución básica de MQL. Tienes que codificarlos tú mismo o encontrarlos en la base de código.

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...
 

Creo que lo he conseguido - estas son las líneas mágicas (espero):

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;

En el manual se utiliza la forma "CSmartLine*mSmartLine = new CSmartLine();" sólo cuando se crea la primera instancia de esta clase.
La próxima vez es sólo "mSmartLine = new CSmartLine();"
Eso no es posible en mi marco de pruebas (errores de compilación). ? ? ?


Escribí un marco de prueba simple con muy pobre funktionality pero la prueba para la creación dinámica de objetos.

Cada vez que el EA detecta una línea de tendencia llamada "bip" - crea un objeto "SmartLine" que renombra la línea y la controla en adelante.

//+------------------------------------------------------------------+
//| 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

No estoy seguro de haber entendido tu idea.

¿Quieres decir que debo ir con los objetos gráficos standarad, escribir una clase que herede cada cosa de ella y aumente la funcionalidad al rango que planeo para mis SmartLines?

He estado pensando en esa idea y me pregunto si MT5 permite crear objetos gráficos en un gráfico (a través de la interfaz normal).

Además de este problema, mis objetos SmartLine necesitan ser activados cuando el precio cambia y no tengo idea de cómo evitar este problema.

¿Tienes alguna experiencia en este campo?

Willbur

 
Willbur:

@Marco

No estoy seguro de haber entendido tu idea.

¿Quieres decir que debo ir con los objetos gráficos estándar, escribir una clase que herede todo de ella y aumente la funcionalidad al rango que planeo para mis SmartLines?

He estado pensando en esa idea y me pregunto si MT5 permite crear objetos gráficos en un gráfico (a través de la interfaz normal).

Además de este problema, mis objetos SmartLine necesitan ser activados cuando el precio cambia y no tengo idea de cómo evitar este problema.

¿Tienes alguna experiencia en este ámbito?

Willbur

Era sólo un ejemplo de cómo hacer las cosas.

Cada vez que construyo una GUI o Interfaz Gráfica de Usuario siempre construyo marcos de control en un bucle.

Esto significa que los botones de control son simplemente creados como B0,B1,B2,B3,B4,B5 y etc que se traduce en botón 0 botón 1 botón 2 y etc.

Y siempre uso IntegerToString() para añadir el entero numérico al nombre del objeto.

Si quieres el trigger también puedes usar:

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
 }

O una variación ya que esto es solo para darte algunas ideas.

Así que básicamente tomas un precio Ask o Bid o Median y lo comparas con el Double en el que se encuentra tu Hline.

El único límite es tu propia imaginación.

 

Marco, Usted todavía está en su EA-Código. ¿Es así?

Esto no es lo que estoy hablando.

La idea era añadir funcionalidad al objeto grafic dado escribiendo mi propio objeto que hereda del objeto grafic original y aumentarlo con las funciones de "comprar" y "vender".

Cuando vaya por este camino mi objeto SmartLine debería aparecer en el menú de MT5 junto a la línea de tendencia, las flechas, el objeto de texto y todas estas cosas.

Donde se puede coger con el ratón como los otros objetos gráficos y añadirlo al gráfico.

Si MT5 permite eso, sólo entonces tenemos que discutir la cuestión restante, cómo el objeto podría ser activado por el programa de la terminal cuando el precio cambia.

Ninguno de los objetos gráficos existentes es capaz de reaccionar a los cambios de precio (hasta donde yo sé).

Willbur

 

No me gusta decir que lo estás haciendo totalmente mal, pero lo haces, porque esto es programación estructural y no OOP. La gran diferencia es el poder de la herencia y la sobrecarga. Y por cierto, no puedes heredar de objetos gráficos reales, pero puedes representar cualquier cosa como un objeto de código y referirte a una línea o lo que sea desde este objeto. Así es como se hace normalmente en cualquier clase, no importa si es una clase MFC o MQL, es todo lo mismo.

Si sus líneas son objetos, entonces trátelos como tales. No trates con arrays fuera, hazlo dentro de una colección de clases y trabaja con punteros. Echa un vistazo a CWndContainer y tener una idea de ella. Esta clase es un contenedor que gestiona principalmente arrays de punteros para objetos CWnd. Ve un paso más allá, tu estructura debería ser

CObject como base para cada objeto

CPriceTimeObjects como base para cada objeto basado en el precio/tiempo, como las líneas, deriva de CObject. Controla la creación, mantiene el tiempo y el precio y llama a un OnCreate(), que puede ser utilizado por el siguiente heredero. También tiene una función Tick que llama a la función virtual OnTick() que luego es sobrecargada por los herederos.

CTrendLine como base para las líneas de tendencia, hereda de CPriceTimeObjects y maneja OnCreate donde crea la línea final usando la función ObjectCreate. También debería tener un manejador OnTick() para reaccionar/responder a los eventos Tick, ya que será sensible al precio por lo que he entendido.

Además de esto, tienes una clase contenedora que gestiona un array de punteros que contiene todos los objetos CTimePriceObject que quieras, hereda también de CTimePriceObject y pasa OnTick() a sus "hijos". El contenedor tiene una función también que maneja el OnChartEvent() para añadir líneas o eliminarlas. También debe tener una función de escaneo para escanear todos los objetos existentes para el caso, el experto fue añadido después de la creación de líneas. Además maneja el OnTick() sobrecargado de CTimePrice, hace un bucle en el array allí, pregunta a cada objeto CTrendLine en él si se siente de alguna manera responsable de reaccionar llamando a la función Tick de cada objeto hijo que es manejado por un OnTick virtual. ¿Por qué esto de nuevo? Porque la CTrendLine sobrecarga esta función también desde CTimePrice y de esta manera esta clase también puede ser heredada por otros herederos con más funciones.

Tu código debería ser como el siguiente

Contenedor CTPContainer;

::OnChartEvent(...)

container.ChartEvent(id, lparam, dparam, sparam) //.. resulta en OnCreate() y OnDelete() en cada CTrendLineObject. El contenedor decide qué hacer, no tu EA.

::OnTick()

container.Tick() // .. resulta en OnTick() en cada objeto "hijo" de CTrendLine

y así sucesivamente.

Esta es una clara base OOP que puede ser fácilmente mejorada con funciones útiles sin volver a tocar ningún EA que utilice estas clases.

 
No me gusta decir que lo estás haciendo totalmente mal pero lo haces.... <br / translate="no">

Wow ... gracias por la lección.

De alguna manera suena, como usted tiene el enfoque rigth.

Voy a tratar de cambiar mi código en esta dirección antes de volver con él.

Willbur

 

Si vas a codificar orientado a objetos de forma consecuente y directa, nunca te arrepentirás. El comienzo puede ser más difícil que la forma normal de arriba hacia abajo, pero usted será capaz de desarrollar en un nivel mucho más alto que incluye más poder, más posibilidades, más flexibilidad y más fácil compatibilidad con los cambios futuros.

MQL es impresionante, pero sin OOP nunca te darás cuenta de lo mucho que es en realidad.

Si tienes alguna pregunta, sólo tienes que postearla y trataré de ayudarte.