Discusión sobre el artículo "Asesor Experto multiplataforma: Señales"

 

Artículo publicado Asesor Experto multiplataforma: Señales:

En este artículo, se discuten las clases CSignal y CSignals que serán utilizadas en los Asesores Expertos multiplataforma. Han sido analizadas las diferencias entre MQL4 y MQL5 respecto a la organización de los datos necesarios para evaluar las señales comerciales obtenidas. Como resultado, tenemos el código compatible con los compiladores de ambas versiones.

Todos los demás cálculos necesarios para la generación de la señal actual han sido desplazados en los objetos de las clases CSignal y CSignals. De esta manera, todo lo que tenemos que realizar es hacer que CSignals ejecute la comprobación, y luego obtener su resultado a través de la llamada a los métodos CheckOpenLong y CheckOpenShort. Las capturas de pantalla de abajo muestran los resultados de la simulación del EA en la plataforma MetaTrader 4 y MetaTrader 5:

(MT4)

signal_ordermanager (MT4)

Autor: Enrico Lambino

 
MetaQuotes Software Corp.:

Nuevo artículo Cross-Platform Expert Advisor: Señales ha sido publicado:

Autor: Enrico Lambino


Hola Enrico. He estado mirando tu trabajo en busca de una solución a mi problema. Al compilar la clase StopBase, obtengo una lista de errores, por favor vea a continuación

implicit conversion from 'number' to 'string'   OrderStopBase.mqh       395     25
implicit conversion from 'number' to 'string'   OrderStopBase.mqh       467     26
implicit enum conversion        OrderStopBase.mqh       468     33
implicit enum conversion        OrderStop.mqh   37      33
implicit conversion from 'number' to 'string'   OrderStop.mqh   44      43
'COrderBase' - import not defined       OrderStop.mqh   54      7
'else' - semicolon expected     OrderStop.mqh   81      4
')' - unexpected token  OrderStop.mqh   81      46
implicit conversion from 'number' to 'string'   StopBase.mqh    767     22
implicit conversion from 'number' to 'string'   StopBase.mqh    774     28
implicit conversion from 'number' to 'string'   StopBase.mqh    796     22
implicit conversion from 'number' to 'string'   StopBase.mqh    803     28
implicit conversion from 'number' to 'string'   StopBase.mqh    698     36
'OrderType' - cannot convert enum       Stop.mqh        227     52
'StopLossCustom' - no one of the overloads can be applied to the function call  Stop.mqh        228     61
could be one of 2 function(s)   Stop.mqh        228     61
   double CStopBase::StopLossCustom(const string,const ENUM_ORDER_TYPE,const double)    StopBase.mqh    136     22
   bool CStopBase::StopLossCustom()     StopBase.mqh    99      22
'OrderType' - cannot convert enum       Stop.mqh        215     71
'TakeProfitCustom' - no one of the overloads can be applied to the function call        Stop.mqh        215     98
could be one of 2 function(s)   Stop.mqh        215     98
   double CStopBase::TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double)  StopBase.mqh    140     22
   bool CStopBase::TakeProfitCustom()   StopBase.mqh    111     22
implicit conversion from 'number' to 'string'   Stop.mqh        250     39
implicit enum conversion        Stop.mqh        291     31
implicit conversion from 'number' to 'string'   Stop.mqh        292     36

Ignorando las advertencias, los errores parecen venir de problemas de tipo de orden. Además, la compilación de su ejemplo signal_ma produce los siguientes errores

'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh        51      39
'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh        58      41
'=' - type mismatch     OrderStopsBase.mqh      106     23
'=' - type mismatch     OrderStopsBase.mqh      108     23
'=' - type mismatch     OrderStopsBase.mqh      110     23
'=' - type mismatch     OrderStopsBase.mqh      180     20
'=' - type mismatch     OrderStopsBase.mqh      182     20
'=' - type mismatch     OrderStopsBase.mqh      184     20

¿De dónde vienen estos errores, por favor, y cómo puedo resolverlos? En un tema relacionado, según tengo entendido, la Sustitución de Liskov estipula que debemos programar para una interfaz, ya que la superclase puede ser sustituida por su clase base. ¿Por qué has elegido pasar objetos a las funciones (que son en sí mismas interfaces), en lugar de utilizar sus clases base? Puede que me equivoque pero creo que esa sería la mejor práctica ¿no?

Gracias por tu ayuda de antemano y por el gran trabajo que has hecho.
 

Hola Shephard, gracias por su comentario. Con respecto a tus preguntas:

  1. Desafortunadamente, no hay manera en este momento para compilar StopBase.mqh por su cuenta. Mientras que los compiladores MQL4 y MQL5 aceptan declaraciones hacia adelante, dan un error si intenta acceder a un método o miembro de la clase que se declaró hacia adelante. Usted tendrá que compilar StopBase.mqh con una clase mucho más grande, como COrderManager o CExpertAdvisor (CStop y CStops es un componente de estas dos clases).
  2. En cuanto a los errores de desajuste de tipo, no los encontré en los archivos originales (¿se modificaron los fuentes en su extremo?).
  3. Creo que en la mayoría de los casos, las clases base se pueden pasar a los métodos de las clases sin problemas. Cuando los expertos en codificación utilizan la biblioteca, sería más fácil asumir que la clase base no existe en absoluto. Pero algunos objetos tienen nuevos métodos virtuales, así como métodos no virtuales. No se puede acceder a estos dos conjuntos de métodos sólo con la clase base. Si utilizas las clases derivadas en lugar de la base, el compilador es capaz de seleccionar la versión correcta de las clases derivadas, por lo que utilizar las clases derivadas es más completo que utilizar sólo las clases base.
 
Enrico Lambino:

Hola Shephard, gracias por tu comentario. Con respecto a tus preguntas:

  1. Desafortunadamente, no hay manera en este momento para compilar StopBase.mqh por su cuenta. Mientras que los compiladores MQL4 y MQL5 aceptan declaraciones hacia adelante, dan un error si intenta acceder a un método o miembro de la clase que se declaró hacia adelante. Usted tendrá que compilar StopBase.mqh con una clase mucho más grande, como COrderManager o CExpertAdvisor (CStop y CStops es un componente de estas dos clases).
  2. En cuanto a los errores de desajuste de tipo, no los encontré en los archivos originales (¿se modificaron los fuentes en su extremo?).
  3. Creo que en la mayoría de los casos, las clases base se pueden pasar a los métodos de las clases sin problemas. Cuando los expertos en codificación utilizan la biblioteca, sería más fácil asumir que la clase base no existe en absoluto. Pero algunos objetos tienen nuevos métodos virtuales, así como métodos no virtuales. No se puede acceder a estos dos conjuntos de métodos sólo con la clase base. Si utilizas las clases derivadas en lugar de la base, el compilador es capaz de seleccionar la versión correcta de las clases derivadas, por lo que utilizar las clases derivadas es más completo que utilizar sólo las clases base.

Hola Enrico,

Muchas gracias por tu rápida respuesta. Entiendo tu razonamiento de pasar clases concretas en lugar de sus clases base. Ciertas implementaciones que son sencillas en C ++ simplemente no son posibles en MQL, pero esa es la manera que es.

Con respecto a los desajustes de tipo, no, yo no he cambiado nada. Simplemente he descargado y compilado. Estaba mirando tu trabajo en busca de un problema molesto que tenía en mi propio sistema. Tu enfoque es realmente genial.

Desarrollé mi sistema así. Consideré un sistema de salida de señal como una estrategia. Digamos que tengo un sistema de cruce MA, si creo un objeto de esto con digamos EURUSD, esto es una estrategia. Agrego esta estrategia en una lista de estrategias. Puedo crear otra estrategia, y añadirla a la lista. Pero las estrategias no son llamadas por OnTick, más bien, son parte de un patrón de Observador. Son actualizadas, o llamadas cuando ocurren eventos específicos, digamos una nueva barra en 5 Minutos, una nueva barra de 10 Pip Renko etc. Tuve problemas con los Stops, lo que desencadenó mi investigación.

Gracias una vez más por tomarse el tiempo para compartir su conocimiento y habilidad, y su pronta respuesta.

 

Hola Shephard,

Gracias por compartir tus sugerencias y puntos de vista.

Creo que lo que está trabajando es una gran idea. Actualmente estoy trabajando en los últimos artículos para esta serie (paradas de orden y paradas incluidas). No estoy seguro de si las clases Stops son un buen ajuste (depende de su implementación). Si puedes hacer que funcionen independientemente del gestor de órdenes, sería bueno porque ayudaría a simplificar tus objetos de clase. Pero espero que te sean útiles en tu trabajo.

En cuanto a la compilación:

  1. Sólo los archivos fuente/encabezado y los archivos de clase base pueden ser compilados sin errores (con la excepción de aquellos que usan declaraciones forward). Esta es la razón por la que en los ejemplos, utilicé la directiva #include en los archivos de cabecera para los archivos base (no los archivos específicos del lenguaje).
  2. Debes usar el compilador correcto (compilar un archivo mq5 con un compilador MQL4 conducirá a errores de compilación).
 

Hola Enrico,

Estoy probando tu módulo de señales. Tengo una señal SHORT y una señal NEUTRAL. ¿Cómo es que al final tengo CMD_VOID? ¿Cuándo aparece CMD_VOID?

En realidad esto aparece en CSignalsBase::Check

if(signal.Entry())
        {
         if(m_signal_open>CMD_VOID)
           {
            ENUM_CMD signal_open=signal.SignalOpen();
            if(m_signal_open==CMD_NEUTRAL)
              {    
               m_signal_open=signal_open;
              }
            else if(m_signal_open!=signal_open)
              {               
               m_signal_open=CMD_VOID;
              }
           }
        }

Sólo hay 2 señales. La señal anterior era CMD_SHORT. La señal actual es CMD_NEUTRAL. ¿Puedes confirmar que CMD_SHORT y CMD_NEUTRAL dan como resultado CMD_VOID?

Si mi primera señal fuera CMD_NEUTRAL y la segunda CMD_SHORT la señal total sería CMD_SHORT. Pero si la primera señal CMD_CORTO y la segunda CMD_NEUTRAL da CMD_VOID.

Supongo que debe ser así:

if(signal.Entry())
        {
         if(m_signal_open>CMD_VOID)
           {
            ENUM_CMD signal_open=signal.SignalOpen();
            if(m_signal_open==CMD_NEUTRAL)
              {    
               m_signal_open=signal_open;
              }
            else if(m_signal_open!=signal_open && signal_open!=CMD_NEUTRAL)
              {               
               m_signal_open=CMD_VOID;
              }
           }
        }
 
if(m_new_signal)
     {
      if(m_signal_open==m_signal_open_last)
         m_signal_open = CMD_NEUTRAL;
      if(m_signal_close==m_signal_close_last)
         m_signal_close= CMD_NEUTRAL;
     }

Esto no es del todo correcto en términos de señales de cierre. ¿Por qué no puede haber dos señales de cierre consecuentes en la misma dirección?

Por ejemplo, tengo señal de entrada Corto, siguiente señal de salida Largo (cerrando corto), siguiente señal de entrada Largo, siguiente señal de entrada Corto, siguiente señal de salida Largo. Así que la última señal para salir de la posición corta no funcionará, ya que es la misma dirección que la señal de salida anterior.

 
mbjen:

Esto no es del todo correcto en términos de señales de cierre. ¿Por qué no puede haber dos señales de cierre consecuentes en la misma dirección?

Por ejemplo, tengo señal de entrada Corto, siguiente señal de salida Largo (cerrando corto), siguiente señal de entrada Largo, siguiente señal de entrada Corto, siguiente señal de salida Largo. Así que la última señal para salir de la posición corta no funcionará, ya que es la misma dirección que la señal de salida anterior.

Depende de lo que usted quería lograr en la forma en que las señales deben ser evaluados.

El último código que ha publicado sólo se ejecuta cuando el miembro de la clase m_new_signal se establece en true. Esto es sólo para el comercio de nuevas señales. Puede establecer este miembro protegido de la clase utilizando el método NewSignal disponible en la clase.

 
Enrico Lambino:

Depende de lo que quieras conseguir sobre cómo deben evaluarse las señales.

El último código que ha publicado sólo se ejecuta cuando el miembro de la clase m_new_signal se establece en true. Esto es sólo para el comercio de nuevas señales. Puede establecer este miembro protegido de la clase utilizando el método NewSignal disponible en la clase.


Yo se eso. Pero si lo pongo en falso también afectará a mis señales de entrada. Está bien para la señal de entrada, pero no para la salida ya que las reglas de salida pueden ser diferentes. Puede ser reversión o señal de salida por lo que 2 señales de salida de la misma dirección es una cosa normal.

 

Parece que en m_signal_close se guarda el valor de la señal anterior en SignalBase.mqh. Por ejemplo tengo alguna señal de salida. Si se comprueba y Calculate() método devuelve false da último valor de la señal que estaba en la salida anterior.

 

Hola mbjen,

mbjen:

Hola Enrico,

Estoy probando tu módulo de señales. Tengo una señal SHORT y una señal NEUTRAL. ¿Cómo es que al final tengo CMD_VOID? ¿Cuándo aparece CMD_VOID?

En realidad esto aparece en CSignalsBase::Check

Sólo hay 2 señales. La señal anterior era CMD_SHORT. La señal actual es CMD_NEUTRAL. ¿Puedes confirmar que CMD_SHORT y CMD_NEUTRAL dan como resultado CMD_VOID?

Si mi primera señal fuera CMD_NEUTRAL y la segunda CMD_SHORT la señal total sería CMD_SHORT. Pero si la primera señal CMD_CORTO y la segunda CMD_NEUTRAL da CMD_VOID.

Supongo que debe ser así:

Siento haber pasado por alto tu primera pregunta. No me había dado cuenta hasta ahora. Era un comentario muy bueno.

Gracias por señalarlo. Sí, tiene razón. Actualizaré el código y revisaré el artículo.

mbjen:

Lo sé. Pero si lo pongo en false también afectará a mis señales de entrada. Está bien para la señal de entrada, pero no para la salida ya que las reglas de salida pueden ser diferentes. Puede ser reversión o señal de salida por lo que 2 señales de salida de la misma dirección es una cosa normal.

Usted tiene un punto aquí. Pero por ejemplo, si sólo quiero dar una señal de salida en un cruce MA, esto sería necesario. Creo que separarlos sería una mejor opción que el código actual:

if(m_new_signal)
 {
  if(m_signal_open==m_signal_open_last)
  m_signal_open = CMD_NEUTRAL;
 }
if(m_new_signal_close)
 {
  if(m_signal_close==m_signal_close_last)
  m_signal_close= CMD_NEUTRAL;
 }
mbjen:

Parece que en m_signal_close se guarda el valor de la señal anterior en SignalBase.mqh. Por ejemplo tengo alguna señal de salida. Si la comprueba y el método Calculate() devuelve false da el último valor de señal que había en la salida anterior.

El método Calculate es un método virtual. Si el método va a devolver false, debe restablecer los miembros de la clase a neutral desde el propio método.