Discusión sobre el artículo "Desarrollamos un Asesor Experto multidivisas (Parte 1): Funcionamiento conjunto de varias estrategias comerciales"

 

Artículo publicado Desarrollamos Asesor Experto multidivisas (Parte 1): Funcionamiento conjunto de varias estrategias comerciales:

Existen bastantes estrategias comerciales distintas. Para diversificar los riesgos y aumentar la estabilidad de los resultados comerciales, puede resultar útil utilizar varias estrategias que funcionen en paralelo. Pero si cada estrategia se implementa como un asesor independiente, se hace mucho más difícil gestionar su trabajo conjunto en una cuenta comercial. Para resolver este problema, es deseable implementar el funcionamiento de diferentes estrategias de negociación en un asesor.

En primer lugar, debemos definir lo que queremos y lo que tenemos.

Tenemos (bueno, o casi):

  • algunas estrategias comerciales diferentes que funcionan en diferentes símbolos y marcos temporales en forma de código de asesor listo para usar o un simple conjunto de reglas formulado para realizar transacciones comerciales
  • un depósito inicial
  • una reducción máxima admisible

Queremos:

  • el funcionamiento conjunto de todas las estrategias seleccionadas en una cuenta en varios símbolos y marcos temporales
  • la distribución del depósito inicial entre todas por igual o según coeficientes establecidos
  • el cálculo automático de los volúmenes de las posiciones abiertas para respetar la tasa máxima de reducción permitida
  • el procesamiento correcto del reinicio del terminal
  • la posibilidad de ejecutar el inicio en MT5 y MT4

Usaremos el enfoque orientado a objetos, MQL5, y el simulador estándar en MetaTrader 5.

El conjunto de tareas es bastante amplio, así que lo resolveremos paso a paso.

Autor: Yuriy Bykov

 
class CStrategy : public CObject {
protected:
   ulong             m_magic;          // Magia
   string            m_symbol;         // Símbolo (instrumento de negociación)
   ENUM_TIMEFRAMES   m_timeframe;      // Periodo del gráfico (timeframe)
   double            m_fixedLot;       // Tamaño de las posiciones abiertas (fijo)

public:
   // Constructor
   CStrategy(ulong p_magic,
             string p_symbol,
             ENUM_TIMEFRAMES p_timeframe,
             double p_fixedLot);

   virtual int       Init() = 0; // Inicialización de la estrategia - procesamiento del evento OnInit
   virtual void      Tick() = 0; // Método principal - Manejo del evento OnTick
};

¿Por qué necesitamos Init-method si hay un constructor?

Por alguna razón, limitaron inmediatamente la clase TS a un símbolo y un marco temporal.


Parece más lógico.

class SYSTEM
{
public:
  virtual void OnTick() {}
};
 
fxsaber #:

¿Por qué necesitas un método Init si tienes un constructor?

Por alguna razón limitaron inmediatamente la clase TS a un símbolo y un marco temporal.

Me gustó el enfoque del autor. Hay un pasaje de este tipo en el artículo:

Los métodos Init() y Tick() se declaran puramente virtuales (después de la cabecera del método es = 0). Esto significa que no vamos a escribir la implementación de estos métodos en la clase CStrategy. Sobre la base de esta clase crearemos clases descendientes, en las que los métodos Init() y Tick() deberán estar necesariamente y contener la implementación de reglas comerciales específicas.

Entonces la clase será abstracta, por lo que entiendo.....

 
Denis Kirichenko #:

Entonces la clase será abstracta, por lo que entiendo.....

Lo será. Por algo se usa en la descendiente. Si no se hace Deinit (hay un destructor), es lógico que no se haga Init (hay un constructor).

//+------------------------------------------------------------------+
//| Constructor|
//+------------------------------------------------------------------+
CSimpleVolumeStrategy::CSimpleVolumeStrategy(
   ulong            p_magic,
   string           p_symbol,
   ENUM_TIMEFRAMES  p_timeframe,
   double           p_fixedLot,
   int              p_signalPeriod,
   double           p_signalDeviation,
   double           p_signaAddlDeviation,
   int              p_openDistance,
   double           p_stopLevel,
   double           p_takeLevel,
   int              p_ordersExpiration,
   int              p_maxCountOfOrders) :
   // Lista de inicialización
   CStrategy(p_magic, p_symbol, p_timeframe, p_fixedLot), // Llamar al constructor de la clase base
   signalPeriod_(p_signalPeriod),
   signalDeviation_(p_signalDeviation),
   signaAddlDeviation_(p_signaAddlDeviation),
   openDistance_(p_openDistance),
   stopLevel_(p_stopLevel),
   takeLevel_(p_takeLevel),
   ordersExpiration_(p_ordersExpiration),
   maxCountOfOrders_(p_maxCountOfOrders)
{}

//+------------------------------------------------------------------+
//| Función de inicialización del experto |
//+------------------------------------------------------------------+
int CSimpleVolumeStrategy::Init() {
// Cargar el indicador para obtener volúmenes de ticks
   iVolumesHandle = iVolumes(m_symbol, m_timeframe, VOLUME_TICK);

// Establecer el tamaño del array-receptor de volúmenes tick y el direccionamiento requerido
   ArrayResize(volumes, signalPeriod_);
   ArraySetAsSeries(volumes, true);

// Establecer número mágico para colocar órdenes a través de comercio
   trade.SetExpertMagicNumber(m_magic);

   return(INIT_SUCCEEDED);
}

Y el estrechamiento fuerte artificial de posibles CTs es una solución extraña.


También se ve claramente el engorro con la entrada por culpa de la POO. Sería bueno eliminarlo.

 

Init() se ha omitido por ahora porque es imposible devolver un resultado desde el constructor. Pero es posible que nunca necesitemos devolver algo que no sea INIT_SUCCESS como resultado de la inicialización de la estrategia. Así que es muy posible que este método se elimine en el futuro.

La asignación de propiedades de estrategia obligatorias en forma de símbolo y marco temporal es una limitación deliberada. Por diseño, la negociación sobre múltiples símbolos se realizará a través del trabajo de muchas instancias de herederos de esta clase, pero cada instancia particular trabaja con un único símbolo. Todavía no he encontrado ninguna estrategia que se vea obstaculizada por esta limitación. Al contrario, estos parámetros se encontraron en cada estrategia considerada, por eso se decidió ponerlos en la clase base de una vez.

Pero en el futuro planeo considerar algunas estrategias multisímbolo que no pueden dividirse en varias estrategias independientes de un solo símbolo (si las hay). No creo que la presencia de las propiedades de símbolo y marco temporal en la clase base dificulte mucho la implementación de una clase hija en la que se utilicen varios símbolos y varios marcos temporales.

 
Yuriy Bykov propiedades de símbolo y marco temporal en la clase base te impida implementar una clase hija que utilice múltiples símbolos y múltiples marcos temporales.

No va a interferir - 100%. Es sólo una entidad innecesaria. La arquitectura OOP sigue el principio de lo general a lo particular. Usted ha hecho el general (clase base) "privado". Aunque todo lo que se llama allí es CStrategy::Tick().

 
Denis Kirichenko #:

Y me gustó el enfoque del autor. Hay un pasaje en el artículo:

Entonces la clase será abstracta, por lo que entiendo.....

Lo es, se utilizará sólo para obtener clases hijo. Usted no tendrá que crear objetos de la clase base CStrategy. Pero cualquier objeto de la clase hija puede ser pasado al objeto Asesor Experto para ser añadido al método CAdvisor::AddStrategy(CStrategy &strategy).

 
Yuriy Bykov #:

Init() se ha omitido por ahora porque es imposible devolver un resultado desde el constructor. Pero es posible que nunca necesitemos devolver algo que no sea INIT_SUCCESS como resultado de la inicialización de la estrategia. Por lo tanto, es muy posible que este método sea eliminado en el futuro.

En caso de que algo saliera mal en el constructor (la manija del indicador no se carga o la memoria no se asigna, etc.), algunas personas mantienen esta variable común.
static int CStrategy::InitFlag = INIT_FAILED;
 
fxsaber #:
En caso de que algo saliera mal en el constructor (no se cargara el manejador del indicador o no se asignara memoria), algunas personas mantienen una variable compartida de este tipo.

Sí, ya he pensado en algo así. Intentaré hacerlo así.

 
Yuriy Bykov #:

cualquier objeto de clase hijo puede ser pasado al objeto EA para ser añadido al método CAdvisor::AddStrategy(CStrategy &strategy).

Parece ser un bug del compilador que no jura esta firma de método cuando se llama así.

   expert.AddStrategy(new CSimpleVolumeStrategy(
                         magic_ + 1, "EURGBP", PERIOD_H1,
                         NormalizeDouble(0.34 * depoPart_, 2),
                         130, 0.9, 1.4, 231, 3750, 50, 600, 3)

Debería ser así.

CAdvisor::AddStrategy(CStrategy* strategy)
 
Hereda de CObject.
class CStrategy : public CObject {

No se utiliza.

class CAdvisor : public CObject {
protected:
   CStrategy         *m_strategies[];  // Conjunto de estrategias de negociación

Me he dado cuenta de que es una práctica bastante común heredar de CObject.