English Русский 中文 Deutsch 日本語 Português
Asesor Experto multiplataforma: las clases CExpertAdvisor y CExpertAdvisor

Asesor Experto multiplataforma: las clases CExpertAdvisor y CExpertAdvisor

MetaTrader 5Ejemplos | 12 diciembre 2017, 13:51
1 520 0
Enrico Lambino
Enrico Lambino

Índice

  1. Introducción
  2. Clase del asesor comercial
  3. Inicialización
  4. Determinando una nueva barra
  5. El manejador OnTick
  6. Contenedor de asesores expertos
  7. Almacenamiento de datos
  8. Ejemplos
  9. Observaciones finales
  10. Conclusión

Introducción

En los ejmplos de las anteriores partes del artículo de la serie  (1, 2, 3, 4, 5, 6, 7, 8, 9) los componentes de los asesores comerciales estaban distribuidos de forma caótica por el principal archivo de encabezado del asesor. Las responsables de ello eran las funciones de usuario. Vamos a finalizar el trabajo de creación de las clases CExpertAdvisor y CExpertsAdvisors. Con su ayuda se organiza una interacción más armoniosa entre los componentes individuales del experto. Asimismo, analizaremos algunas cuestiones comunes en el uso de los asesores expertos: la carga y el almacenamiento de datos cambiantes y la detección de nuevas barras.

Clase del asesor comercial

La clase CExpertAdvisorBase se muestra en el código más abajo. Hasta el momento, la mayoría de diferencias entre MQL4 y MQL5 se suavizaban mediante el procesamiento con otros objetos de clase. Estos han sido descritos en los anteriores artículos de la serie.

class CExpertAdvisorBase : public CObject
  {
protected:
   //--- parámetros de comercio
   bool              m_active;
   string            m_name;
   int               m_distance;
   double            m_distance_factor_long;
   double            m_distance_factor_short;
   bool              m_on_tick_process;
   //--- parámetros de la señal
   bool              m_every_tick;
   bool              m_one_trade_per_candle;
   datetime          m_last_trade_time;
   string            m_symbol_name;
   int               m_period;
   bool              m_position_reverse;
   //--- objetos de las señales
   CSignals         *m_signals;
   //--- objetos comerciales   
   CAccountInfo      m_account;
   CSymbolManager    m_symbol_man;
   COrderManager     m_order_man;
   //--- objetos de tiempo de comercio
   CTimes           *m_times;
   //--- vela
   CCandleManager    m_candle_man;
   //--- evento
   CEventAggregator *m_event_man;
   //--- contenedor
   CObject          *m_container;
public:
                     CExpertAdvisorBase(void);
                    ~CExpertAdvisorBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_EXPERT;}
   //--- inicialización
   bool              AddEventAggregator(CEventAggregator*);
   bool              AddMoneys(CMoneys*);
   bool              AddSignal(CSignals*);
   bool              AddStops(CStops*);
   bool              AddSymbol(const string);
   bool              AddTimes(CTimes*);
   virtual bool      Init(const string,const int,const int,const bool,const bool,const bool);
   virtual bool      InitAccount(void);
   virtual bool      InitCandleManager(void);
   virtual bool      InitEventAggregator(void);
   virtual bool      InitComponents(void);
   virtual bool      InitSignals(void);
   virtual bool      InitTimes(void);
   virtual bool      InitOrderManager(void);
   virtual bool      Validate(void) const;
   //--- contenedor
   void              SetContainer(CObject*);
   CObject          *GetContainer(void);
   //--- activación y desactivación
   bool              Active(void) const;
   void              Active(const bool);
   //--- métodos de colocación y obtención       
   string            Name(void) const;
   void              Name(const string);
   int               Distance(void) const;
   void              Distance(const int);
   double            DistanceFactorLong(void) const;
   void              DistanceFactorLong(const double);
   double            DistanceFactorShort(void) const;
   void              DistanceFactorShort(const double);
   string            SymbolName(void) const;
   void              SymbolName(const string);
   //--- punteros a los objetos
   CAccountInfo     *AccountInfo(void);
   CStop            *MainStop(void);
   CMoneys          *Moneys(void);
   COrders          *Orders(void);
   COrders          *OrdersHistory(void);
   CStops           *Stops(void);
   CSignals         *Signals(void);
   CTimes           *Times(void);
   //--- administrador de órdenes
   string            Comment(void) const;
   void              Comment(const string);
   bool              EnableTrade(void) const;
   void              EnableTrade(bool);
   bool              EnableLong(void) const;
   void              EnableLong(bool);
   bool              EnableShort(void) const;
   void              EnableShort(bool);
   int               Expiration(void) const;
   void              Expiration(const int);
   double            LotSize(void) const;
   void              LotSize(const double);
   int               MaxOrdersHistory(void) const;
   void              MaxOrdersHistory(const int);
   int               Magic(void) const;
   void              Magic(const int);
   uint              MaxTrades(void) const;
   void              MaxTrades(const int);
   int               MaxOrders(void) const;
   void              MaxOrders(const int);
   int               OrdersTotal(void) const;
   int               OrdersHistoryTotal(void) const;
   int               TradesTotal(void) const;
   //--- administrador de señales   
   int               Period(void) const;
   void              Period(const int);
   bool              EveryTick(void) const;
   void              EveryTick(const bool);
   bool              OneTradePerCandle(void) const;
   void              OneTradePerCandle(const bool);
   bool              PositionReverse(void) const;
   void              PositionReverse(const bool);
   //--- velas adicionales
   void              AddCandle(const string,const int);
   //--- detección de una nueva barra
   void              DetectNewBars(void);
   //-- evento
   virtual bool      OnTick(void);
   virtual void      OnChartEvent(const int,const long&,const double&,const string&);
   virtual void      OnTimer(void);
   virtual void      OnTrade(void);
   virtual void      OnDeinit(const int,const int);
   //--- restauración
   virtual bool      Save(const int);
   virtual bool      Load(const int);

protected:
   //--- administrador de velas   
   virtual bool      IsNewBar(const string,const int);
   //--- administrador de órdenes
   virtual void      ManageOrders(void);
   virtual void      ManageOrdersHistory(void);
   virtual void      OnTradeTransaction(COrder*) {}
   virtual datetime  Time(const int);
   virtual bool      TradeOpen(const string,const ENUM_ORDER_TYPE,double,bool);
   //--- administrador de símbolos
   virtual bool      RefreshRates(void);
   //--- desinicialización
   void              DeinitAccount(void);
   void              DeinitCandle(void);
   void              DeinitSignals(void);
   void              DeinitSymbol(void);
   void              DeinitTimes(void);
  };

La mayoría de los métodos declarados en esta clase cumplen el papel de envoltorio de los métodos de sus componentes. Los métodos clave de la clase se describen más abajo

Inicialización

En la fase de desinicialización del asesor, en primer lugar hay que crear los objetos necesarios para que la estrategia comercial funcione (money-management, señales, etcétera) e integrarlos con el ejemplar СExpertAdvisor, que también se debe crear en OnInit. Como resultado, al iniciar en el asesor cualquier función, necesitaremos una sola línea de código, que llamará el manejador correspondiente o el método del ejemplar CExpertAdvisor. Es algo muy parecido al método de uso de CExpert de la Biblioteca Estándar MQL5.

Después de crear el ejemplar CExpertAdvisor, se llama su método Init. Aquí tenemos el código de este método:

bool CExpertAdvisorBase::Init(string symbol,int period,int magic,bool every_tick=true,bool one_trade_per_candle=true,bool position_reverse=true)
  {
   m_symbol_name=symbol;
   CSymbolInfo *instrument;
   if((instrument=new CSymbolInfo)==NULL)
      return false;
   if(symbol==NULL) symbol=Symbol();
   if(!instrument.Name(symbol))
      return false;
   instrument.Refresh();
   m_symbol_man.Add(instrument);
   m_symbol_man.SetPrimary(m_symbol_name);
   m_period=(ENUM_TIMEFRAMES)period;
   m_every_tick=every_tick;
   m_order_man.Magic(magic);
   m_position_reverse=position_reverse;
   m_one_trade_per_candle=one_trade_per_candle;
   CCandle *candle=new CCandle();
   candle.Init(instrument,m_period);
   m_candle_man.Add(candle);
   Magic(magic);
   return false;
  }

Aquí creamos los ejemplares de la mayoría de los componentes, que con frecuencia se encuentran en las estrategias comerciales. Asimismo, aquí se incluye el símbolo o el instrumento a usar (debe ser transformado en un tipo de objeto) y el periodo/marco temporal por defecto. Aquí se indican las normas:

  • sobre si deben ejecutarse las tareas esenciales de la estrategia en cada tick o solo en el tick inicial de cada vela;
  • sobre si es necesario indicar un límite máximo - una transacción por vela (para prevenir las entradas múltiples en el transcurso de una vela);
  • sobre si debe virar la posición al recibir una señal opuesta (cerrarse la transacción actual y abrirse una nueva basada en la nueva señal).

Al final de la función OnInit, el ejemplar CExpertAdvisor debe llamar su método InitComponents. El fragmento de código que vemos más abajo demuestra el método indicado para CExpertBase:

bool CExpertAdvisorBase::InitComponents(void)
  {
   if(!InitSignals())
     {
      Print(__FUNCTION__+": error in signal initialization");
      return false;
     }
   if(!InitTimes())
     {
      Print(__FUNCTION__+": error in time initialization");
      return false;
     }
   if(!InitOrderManager())
     {
      Print(__FUNCTION__+": error in order manager initialization");
      return false;
     }
   if(!InitCandleManager())
     {
      Print(__FUNCTION__+": error in candle manager initialization");
      return false;
     }
   if(!InitEventAggregator())
     {
      Print(__FUNCTION__+": error in event aggregator initialization");
      return false;
     }
   return true;
  }

En este método se llaman los métodos Init de todos los componentes del ejemplar del asesor. De una forma análoga, en este método se llaman los métodos Validate para cada componente, para comprobar si superarán sus ajustes la validación.

Determinando una nueva barra

Algunas estrategias comerciales funcionan en el primer tick de la nueva vela. Hay muchas formas de implementar esta función. Una de ellas es comparar la hora y el precio de apertura de la vela actual con su estado anterior. Este método se implementa en la clase CCandle. El siguiente fragmento de código muestra la declaración de la clase CCandleBase, básica para CCandle:

class CCandleBase : public CObject
  {
protected:
   bool              m_new;
   bool              m_wait_for_new;
   bool              m_trade_processed;
   int               m_period;
   bool              m_active;
   MqlRates          m_last;
   CSymbolInfo      *m_symbol;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CCandleBase(void);
                    ~CCandleBase(void);
   virtual int       Type(void) const {return(CLASS_TYPE_CANDLE);}
   virtual bool      Init(CSymbolInfo*,const int);
   virtual bool      Init(CEventAggregator*);
   CObject          *GetContainer(void);
   void              SetContainer(CObject*);
   //--- métodos de colocación y obtención
   void              Active(bool);
   bool              Active(void) const;
   datetime          LastTime(void) const;
   double            LastOpen(void) const;
   double            LastHigh(void) const;
   double            LastLow(void) const;
   double            LastClose(void) const;
   string            SymbolName(void) const;
   int               Timeframe(void) const;
   void              WaitForNew(bool);
   bool              WaitForNew(void) const;
   //--- procesamiento
   virtual bool      TradeProcessed(void) const;
   virtual void      TradeProcessed(bool);
   virtual void      Check(void);
   virtual void      IsNewCandle(bool);
   virtual bool      IsNewCandle(void) const;
   virtual bool      Compare(MqlRates &) const;
   //--- restauración
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

La comprobación de la aparición de una nueva vela en el gráfico se lleva a cabo mediante Check:

CCandleBase::Check(void)
  {
   if(!Active())
      return;
   IsNewCandle(false);
   MqlRates rates[];
   if(CopyRates(m_symbol.Name(),(ENUM_TIMEFRAMES)m_period,1,1,rates)==-1)
      return;
   if(Compare(rates[0]))
     {
      IsNewCandle(true);
      TradeProcessed(false);
      m_last=rates[0];
     }
  }

Al comprobar la nueva barra, el ejemplar del asesor debe llamar a este método en cada tick. Después, el programador puede ampliar CCxpertAdvisor de tal forma que al aparecer una nueva vela, pueda realizar diferentes tareas en el gráfico.

Como se muestra en el código más arriba, la comparación real de la hora y el precio de la barra se realiza con el método Compare de esta clase, como se muestra en el siguiente código:

bool CCandleBase::Compare(MqlRates &rates) const
  {
   return (m_last.time!=rates.time ||
           (m_last.open/m_symbol.TickSize())!=(rates.open/m_symbol.TickSize()) || 
           (!m_wait_for_new && m_last.time==0));
  }

Este método de comprobación de una nueva barra depende de tres condiciones. El cumplimiento de al menos una de ellas garantiza el resultado true, lo que indica que una nueva vela ha aparecido en el gráfico:

  1. La última hora registrada no es igual a la hora de apertura de la barra actual.
  2. El último precio de apertura registrado no coincide con el precio de apertura de la barra actual.
  3. La última hora de apertura registrada es cero, y el nuevo tick no debe ser el primer tick de esta vela.

Las dos primeras condiciones implican una comparación directa del valor de la barra actual con el registro anterior. La tercera condición se aplica solo al primer tick, que será registrado por el experto. En el momento en que el asesor ha sido cargado en el gráfico, no dispone aún del registro anterior (valores de hora y precio de apertura). De esta forma, la última hora anotada será igual a cero. Algunos tráders analizan esta barra como nueva en sus asesores. Otros prefieren que el experto espere a que aparezca una nueva barra tras la inicialización del asesor.

Al igual que sucede con los otros tipos de clases que hemos descrito anteriormente, la clase CCandle también debe tener su contenedor, CCandleManager. En el código de más abajo se muestra la declaración de la clase СCandleManagerBase:

class CCandleManagerBase : public CArrayObj
  {
protected:
   bool              m_active;
   CSymbolManager   *m_symbol_man;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CCandleManagerBase(void);
                    ~CCandleManagerBase(void);
   virtual int       Type(void) const {return(CLASS_TYPE_CANDLE_MANAGER);}
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual bool      Add(const string,const int);
   CObject          *GetContainer(void);
   void              SetContainer(CObject *container);
   bool              Active(void) const;
   void              Active(bool active);
   virtual void      Check(void) const;
   virtual bool      IsNewCandle(const string,const int) const;
   virtual CCandle *Get(const string,const int) const;
   virtual bool      TradeProcessed(const string,const int) const;
   virtual void      TradeProcessed(const string,const int,const bool) const;
   //--- recovery
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

El ejemplar de la clase СCandle se crea usando como base el nombre del instrumento y el marco temporal. La presencia de CCandleManager hace más sencillo para el asesor monitorear varios gráficos del instrumento indicado, por ejemplo, podemos comprobar la aparición de una nueva vela en EURUSD M15 y EURUSD H1 en el mismo asesor. Los ejemplares de CCandle con símbolo y marco temporal idénticos están de más, por lo que es recomandable evitarlos. Durante la búsqueda de un ejemplar específico, CCandle solo tiene que llamar al método apropiado, que está en CCandleManager, y especificar el símbolo y el marco temporal. A su vez, CCandleManager, encontrará el correspondiente ejemplar de CCandle y llamará al método destinado a él.

Además de la comprobación de una nueva vela, CCandle y CCandleManager cumplen con otra función: comprueban si el asesor ha abierto una nueva transacción del símbolo y marco temporal dados. Es posible comprobar las transacciones recientes solo por el símbolo, sin tener en cuenta el marco temporal. Para implementar este comportamiento, el propio ejemplar de CExpertAdvisor debe establecer/resetear la bandera responsable de ello. El interrumpor para ambas clases se puede implementar con el uso del método TradeProcessed.

Al gestionar las velas, los métodos TradeProcessed (tanto para establecer como para recibir) funcionan solo para encontrar el ejemplar solicitado de CCandle y aplicar el valor correspondiente:

bool CCandleManagerBase::TradeProcessed(const string symbol,const int timeframe) const
  {
   CCandle *candle=Get(symbol,timeframe);
   if(CheckPointer(candle))
      return candle.TradeProcessed();
   return false;
  }

CCandle asigna un nuevo valor a uno de sus miembros de clase, m_trade_processed. Los responsables de ello son los métodos mostrados más abajo:

bool CCandleBase::TradeProcessed(void) const
  {
   return m_trade_processed;
  }

CCandleBase::TradeProcessed(bool value)
  {
   m_trade_processed=value;
  }

El manejador OnTick

El método OnTick de la clase CExpertAdvisor se usa con mayor frecuencia en esta clase. Precisamente él ejecuta la mayor parte de las acciones. La principal operación de este método se muestra en el diagrama:


CExpertAdvisorBase OnTick


El proceso comienza con la conmutación de la bandera en el asesor. Esto previene el procesamiento repetido del tick. El método OnTick, en condiciones ideales, se llama solo dentro de la función del evento OnTick, pero también puede ser llamado por otros medios, por ejemplo, con OnChartEvent. En ausencia de esta bandera, el método OnTick de la clase puede ser llamado cuando el procesamiento del tick anterior aún no ha terminado. Entonces el tick puede ser procesado más de una vez, y si en él se genera una transacción, esta también puede ser duplicada.

La actualización de los datos es necesaria para que el asesor tenga acceso a los datos más recientes y no tenga que pasar al procesamiento del tick anterior. Si el asesor no logra actualizar los datos, resetea la bandera de procesamiento del tick, finaliza el funcionamiento del método y comienza a esperar un nuevo tick.

El siguiente paso es buscar nuevas barras y comprobar las señales comerciales. Esta comprobación se realiza por defecto en cada tick. Sin embargo, podemos ampliar este método de tal forma que realice esta comprobación solo cuando se haya registrado la llegada de una nueva señal (para acelerar el tiempo de procesamiento, especialmente durante los back-tests y la optimización).

Asimismo, en la clase se encuentra el miembro de la clase m_position_reverse, responsable del viraje de la posición en la dirección opuesta a la señal recibida. Este viraje se realiza aquí solo para neutralizar la posición actual. En MetaTrader 4 y en el modo de cobertura de MetaTrader 5 el miembro de la clase procesa la salida de las transacciones opuestas a la señal recibida (las transacciones cuya dirección se corresponde con la dirección de la señal actual, no se cierran). En el sistema de compensación de MetaTrader 5 puede haber solo una posición. De esta forma, el asesor abrirá una posición nueva con un volumen análogo a la transacción actual, pero en dirección opuesta.

La señal comercial se procesa básicamente usando m_signals, pero hay otros factores que pueden prevenir la apertura de una nueva transacción por parte del asesor, como por ejemplo, el comercio solo en una nueva barra o los filtros temporales. El experto obtine la posibilidad de abrir una nueva operación solo cuando se cumplen todas las condiciones.

Al final del procesamiento del tick, el asesor pondrá la bandera de tick en false, y después de ello, se le permitirá procesar otro tick.

Contenedor de asesores expertos

De forma análoga a los otros objetos de clase descritos en las anteriores partes del artículo, la clase CExpertAdvisor también tiene su propio contenedor, CExpertAdvisors. En el fragmento de código que vemos a continuación se muestra la declaración de su clase básica, CExpertAdvisorsBase:

class CExpertAdvisorsBase : public CArrayObj
  {
protected:
   bool              m_active;
   int               m_uninit_reason;
   CObject          *m_container;
public:
                     CExpertAdvisorsBase(void);
                    ~CExpertAdvisorsBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_EXPERTS;}
   virtual int       UninitializeReason(void) const {return m_uninit_reason;}
   //--- métodos de establecimiento y obtención
   void              SetContainer(CObject *container);
   CObject          *GetContainer(void);
   bool              Active(void) const;
   void              Active(const bool);
   int               OrdersTotal(void) const;
   int               OrdersHistoryTotal(void) const;
   int               TradesTotal(void) const;
   //--- inicialización
   virtual bool      Validate(void) const;
   virtual bool      InitComponents(void) const;
   //--- evento
   virtual void      OnTick(void);
   virtual void      OnChartEvent(const int,const long&,const double&,const string&);
   virtual void      OnTimer(void);
   virtual void      OnTrade(void);
   virtual void      OnDeinit(const int,const int);
   //--- restauración
   virtual bool      CreateElement(const int);
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

Este contenedor refleja principalmente los métodos públicos de la clase. Un ejemplo de ello es el manejador OnTick. El método simplemente itera por cada ejemplar de СExpertAdvisor para llamar su método OnTick:

void CExpertAdvisorsBase::OnTick(void)
  {
   if(!Active()) return;
   for(int i=0;i<Total();i++)
     {
      CExpertAdvisor *e=At(i);
      e.OnTick();
     }
  }

La presencia de este contenedor da la posibilidad de guardar multitud de ejemplares de CExpertAdvisor. Se trata, posiblemente, del único método para iniciar varios asesores en un gráfico. Solo tendrá que inicializar varios ejemplares de CExpertAdvisor, guardar sus punteros a un contenedor CExpertAdvisors, y después usar el método OnTick del contenedor, para que los métodos OnTick de cada ejemplar CExpertAdvisor se activen. Lo mismo se puede hacer para cada ejemplar de la clase CExpert de la Biblioteca estándar MQL5, usando la clase CArrayObj o sus herederos.

Almacenamiento de datos

Algunos datos que se usan en los ejemplares CExpertAdvisor, se guardan solo en la memoria de la computadora. Normalmente, los datos necesarios se guardan en la plataforma, y el asesor los recibe desde ella con la ayuda de la función de llamada. Sin embargo, para los datos que se crean dinámicamente mientras el asesor está en marcha, esto no es así. Cuando el experto inicia el evento OnDeinit, el asesor elimina todos los objetos y de esta forma pierde sus datos.

OnDeinit puede ser iniciado en varios casos: al cerrar la plataforma completa (MetaTrader 4 o MetaTrader 5), al eliminar el experto del gráfico o cuando sucede la nueva compilación del asesor tras cambiar su código. La lista completa de los posibles eventos que pueden causar la desinicialización se puede ver utilizando UninitializeReason. Cuando el asesor no tiene acceso a estos datos, es muy probable que se comporte como si se hubiese cargado por primera vez en el gráfico.

La mayoría de los datos volátiles en la clase CExpertAdvisor puede encontrarse en uno de sus miembros, que es una instancia COrderManager. Aquí se crean los ejemplares COrder y COrderStop (y sus descendientes), al tiempo que el experto cumple con su trabajo habitual. Puesto que dichos ejemplares se crean de forma dinámica usando OnTick, estos no se restablecen cuando el asesor se inicializa de nuevo. Por eso, en el asesor debe implementarse un método de guardado y extracción de estos datos cambiantes. Uno de los métodos de implementación de esta acción consiste en usar un heredero de la clase CFileBin, CExpertFile. En el código descrito más abajo se muestra la declaración de su clase básica, CExpertFileBase.

class CExpertFileBase : public CFileBin
  {
public:
                     CExpertFileBase(void);
                    ~CExpertFileBase(void);
   void              Handle(const int handle) { m_handle=handle; };
   uint              WriteBool(const bool value);
   bool              ReadBool(bool &value);
  };

Aquí expandimos CFileBin, para declarar de forma explícita los métodos de registro y cálculo de los datos del tipo bool.

Al final del archivo de esta clase declaramos un ejemplar de la clase CExpertFile. Este ejemplar se usará todo el tiempo para el funcionamiento del asesor, si este debe guardar y cargar los datos cambiantes. Como alternativa, podemos simplemente confiar en los métodos Save y Load, heredados de CObject, y procesar el guardado y la carga de forma habitual. Pero puede ser un trabajo muy meticuloso. Podremos ahorrar muchas fuerzas y líneas de código si usamos solo CFile (y sus herederos).

//CExpertFileBase class definition
//+------------------------------------------------------------------+ 
#ifdef __MQL5__
#include "..\..\MQL5\File\ExpertFile.mqh"
#else
#include "..\..\MQL4\File\ExpertFile.mqh"
#endif
//+------------------------------------------------------------------+ 
CExpertFile file;
//+------------------------------------------------------------------+ 

El gestor de órdenes guarda los datos cambiantes con el método Save:

bool COrderManagerBase::Save(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   file.WriteDouble(m_lotsize);
   file.WriteString(m_comment);
   file.WriteInteger(m_expiration);
   file.WriteInteger(m_history_count);
   file.WriteInteger(m_max_orders_history);
   file.WriteBool(m_trade_allowed);
   file.WriteBool(m_long_allowed);
   file.WriteBool(m_short_allowed);
   file.WriteInteger(m_max_orders);
   file.WriteInteger(m_max_trades);
   file.WriteObject(GetPointer(m_orders));
   file.WriteObject(GetPointer(m_orders_history));
   return true;
  }

La mayor parte de estos datos son tipos primitivos, excepto los dos últimos, que son los contenedores de las órdenes actuales y las históricas. Para estos datos, se usa el método WriteObject de la clase CFileBin, que simplemente llama el método Save del objeto que hay que escribir. En el código de más abajo se muestra el métod Save de la clase COrderBase:

bool COrderBase::Save(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   file.WriteBool(m_initialized);
   file.WriteBool(m_closed);
   file.WriteBool(m_suspend);
   file.WriteInteger(m_magic);
   file.WriteDouble(m_price);
   file.WriteLong(m_ticket);
   file.WriteEnum(m_type);
   file.WriteDouble(m_volume);
   file.WriteDouble(m_volume_initial);
   file.WriteString(m_symbol);
   file.WriteObject(GetPointer(m_order_stops));
   return true;
  }

Como podemos ver aquí, el proceso simplemente se repite al guardar los objetos. Los tipos de datos primitivos se guardan en un archivo, como siempre. Para los datos de tipo complejo, el método Save del objeto se llama a través del método WriteObject de la clase CFileBin.

En los casos en los que tenemos ante nosotros varios ejemplares de CExpertAdvisor, el contenedor CExpertAdvisors también será capaz de guardar datos:

bool CExpertAdvisorsBase::Save(const int handle)
  {
   if(handle!=INVALID_HANDLE)
     {
      for(int i=0;i<Total();i++)
        {
         CExpertAdvisor *e=At(i);
         if(!e.Save(handle))
            return false;
        }
     }
   return true;
  }

Los métodos Save se llaman para cada ejemplar de CExpertAdvisor. El único manejador de archivo significa que para cada archivo de un asesor experto debe haber solo un archivo de guardado. Cada ejemplar de CExpertAdvisor podría tener su propio archivo de guardado, pero esto supondría un enfoque más complicado.

La carga de datos es la parte más complicada. Al ejecutar el guardado, los valores de ciertos miembros de la clase se anotan en un archivo. Al cargar estos datos, los ejemplares del objeto deberán ser restablecidos, y, si fuera posible, en el mismo estado que tenían antes del guardado. El código siguiente muestra el método Load del gestor de órdenes.

bool COrderManagerBase::Load(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   if(!file.ReadDouble(m_lotsize))
      return false;
   if(!file.ReadString(m_comment))
      return false;
   if(!file.ReadInteger(m_expiration))
      return false;
   if(!file.ReadInteger(m_history_count))
      return false;
   if(!file.ReadInteger(m_max_orders_history))
      return false;
   if(!file.ReadBool(m_trade_allowed))
      return false;
   if(!file.ReadBool(m_long_allowed))
      return false;
   if(!file.ReadBool(m_short_allowed))
      return false;
   if(!file.ReadInteger(m_max_orders))
      return false;
   if(!file.ReadInteger(m_max_trades))
      return false;
   if(!file.ReadObject(GetPointer(m_orders)))
      return false;
   if(!file.ReadObject(GetPointer(m_orders_history)))
      return false;
   for(int i=0;i<m_orders.Total();i++)
     {
      COrder *order=m_orders.At(i);
      if(!CheckPointer(order))
         continue;
      COrderStops *orderstops=order.OrderStops();
      if(!CheckPointer(orderstops))
         continue;
      for(int j=0;j<orderstops.Total();j++)
        {
         COrderStop *orderstop=orderstops.At(j);
         if(!CheckPointer(orderstop))
            continue;
         for(int k=0;k<m_stops.Total();k++)
           {
            CStop *stop=m_stops.At(k);
            if(!CheckPointer(stop))
               continue;
            orderstop.Order(order);
            if(StringCompare(orderstop.StopName(),stop.Name())==0)
              {
               orderstop.Stop(stop);
               orderstop.Recreate();
              }
           }
        }
     }
   return true;
  }

Como podemos ver, el código del método en COrderManager es más complejo, a diferencia del método Load análogo en la clase CExpertAdvisor. El motivo es que, a diferencia del gestor de órdenes, los ejemplares de CExpertAdvisor se crean a través de OnInit, así que le contenedor solo necesita llamar los métodos Load para cada ejmplar CExpertAdvisor, en lugar de usar el método ReadObject de la clase CFileBin.

Los ejemplares de la clase que no hayan sido creado durante la ejecución de OnInit, deberán irse creando a medida que el asesor se cargue de nuevo. Esto se logra expandiendo el método CreateElement de la clase CArrayObj. El objeto no puede crearse a sí mismo, por lo que debe ser creado por su objeto primario o contenedor, o incluso desde el archivo de encabezamiento del código fuente. Podemos ver un ejemplo en el método ampliado de CreateElement, que se encuentra en COrdersBase. En esta clase, el contendor es COrders (heredero de la clase COrdersBase), y el objeto creado tiene el tipo COrder:

bool COrdersBase::CreateElement(const int index)
  {
   COrder*order=new COrder();
   if(!CheckPointer(order))
      return(false);
   order.SetContainer(GetPointer(this));
   if(!Reserve(1))
      return(false);
   m_data[index]=order;
   m_sort_mode=-1;
   return CheckPointer(m_data[index]);
  }

Aquí, aparte del elemento de creación, también establecemos su objeto ancestro o contenedor, para distinguir si pertenece a la lista de transacciones activas (miembro m_orders de la clase COrderManagerBase) o históricas (miembro m_orders_history de la clase COrderManagerBase).

Ejemplos

Los ejemplos №№ 1 — 4 de este artículo son versiones cambiadas de los cuatro ejemplos del artículo anterior (ver Asesor Experto Multiplataforma: Stops Personalizados, Ausencia de Pérdidas y Trailing). Vamos a echar un vistazo a un ejemplo más complejo, expert_custom_trail_ha_ma.mqh, que es una variante modificada de custom_trail_ha_ma.mqh.

Antes de la función OnInit, declaramos los siguientes ejemplares de objetos globales:

COrderManager *order_manager;
CSymbolManager *symbol_manager;
CSymbolInfo *symbol_info;
CSignals *signals;
CMoneys *money_manager;
CTimes *time_filters;

Lo sustituimos por el ejemplar CExpert. Algunos de los ejemplares enumerados más arriba se encuentran en el propio CExpetAdvisor (por ejemplo, COrderManager), el resto deben ser creados por el método OnInit (contenedores de las clases):

CExpertAdvisors experts;

Al principio del método creamos el ejemplar CExpertAdvisor. Asimismo, llamamos el método Init con los ajustes básicos:

int OnInit()
  {
//--- 
   CExpertAdvisor *expert=new CExpertAdvisor();
   expert.Init(Symbol(),Period(),12345,true,true,true);
//--- other code
//--- 
   return(INIT_SUCCEEDED);
  }

Ya no es necesario crear CSymbolInfo / CSymbolManager, puesto que los ejemplares de la clase CExpertAdvisor pueden crear ellos mismos los ejemplares de estas clases.

La función definida por el usuario también debe ser eliminada, nuestro experto ya no la necesita.

También eliminamos del código la declaración global de los contenedores, puesto que ellos deben ser declarados en OnInit. Un ejemplo de ello es el contenedor de filtros temporales (CTimeFilters), que se encuentra en la función OnInit:

CTimes *time_filters=new CTimes();

Los punteros a los contenedores, que antes fueron añadidos al gestor de órdenes, ahora, en lugar de ello, se añaden al ejemplar de CExpertAdvisor. Todos los demás contenedores que no hayan sido añadidos al gestor de órdenes, también deberán ser añadidos al ejemplar CExpertAdvisor. Precisamente COrderManager guarda estos punteros. El ejemplar de CExpertAdvisor crea solo los métodos de envoltorio.

Después de ello, añadimos el ejemplar de CExpertAdvisor al ejemplar CExpertAdvisors. A continuación, llamamos el método InitComponents del ejemplar CExpertAdvisors. Esto garantiza la inicialización de todos los ejemplares de CExpertAdvisor y sus componentes.

int OnInit()
  {
//--- 
//--- otro código
   experts.Add(GetPointer(expert));
   if(!experts.InitComponents())
      return(INIT_FAILED);
//--- otro código
//--- 
   return(INIT_SUCCEEDED);
  }

Y al fin, insertamos el código que posibilita la nueva carga del experto, si su trabajo ha sido interrumpido.

int OnInit()
  {
//--- 
//--- otro código 
 file.Open(savefile,FILE_READ);
   if(!experts.Load(file.Handle()))
      return(INIT_FAILED);
   file.Close();
//--- 
   return(INIT_SUCCEEDED);
  }

Si el asesor no se carga desde el archivo, retorna INIT_FAILED. Sin embargo, si no está el archivo de guardado (y se genera INVALID_HANDLE), el asesor no tendrá error de inicialización, puesto que los métodos Load de las clases CExpertAdvisors y CExpertAdvisor retornan true después de recibir un manejador no válido. Este enfoque conlleva un determinado riesgo, pero es muy poco probable que el archivo guardado sea abierto por otro programa. Simplemente, asegúrese de que cada ejemplar del asesor que esté iniciado en el gráfico tenga un archivo de guardado aparte (como sucede con el número mágico de la orden).

El quinto ejemplo no lo hemos tomado del artículo anterior. En lugar de ello, los cuatro asesores de este artículo se han combinado en un experto dentro de él. En él se usa una versión ligeramente modificada de la función OnInit para cada uno de estos asesores, que se declara como "definida por el usuario". Su valor devuelto tiene el tipo CExpertAdvisor*. Si no se ha logrado crear el experto, se retorna NULL en lugar de INIT_SUCCEEDED. El fragmento de código representado más abajo muestra la función actualizada OnInit del archivo de encabezamiento del experto combinado:

int OnInit()
  {
//--- 
   CExpertAdvisor *expert1=expert_breakeven_ha_ma();
   CExpertAdvisor *expert2=expert_trail_ha_ma();
   CExpertAdvisor *expert3=expert_custom_stop_ha_ma();
   CExpertAdvisor *expert4=expert_custom_trail_ha_ma();
      
   if (!CheckPointer(expert1))
      return INIT_FAILED;
   if (!CheckPointer(expert2))
      return INIT_FAILED;
   if (!CheckPointer(expert3))
      return INIT_FAILED;
   if (!CheckPointer(expert4))
      return INIT_FAILED;
   
   experts.Add(GetPointer(expert1));
   experts.Add(GetPointer(expert2));
   experts.Add(GetPointer(expert3));
   experts.Add(GetPointer(expert4));   
   
   if(!experts.InitComponents())
      return(INIT_FAILED);
   file.Open(savefile,FILE_READ);
   if(!experts.Load(file.Handle()))
      return(INIT_FAILED);
   file.Close();
//--- 
   return(INIT_SUCCEEDED);
  }

El experto comienza el trabajo con la inicialización de cada ejemplar de CExpertAdvisor. A continuación, pasa a la comprobación de cada uno de los punteros a CExpertAdvisor. Si el puntero no es dinámico, la función retorna INIT_FAILED, y la inicialización no tendrá éxito. Si cada uno de los ejemplares supera la comprobación de los punteros, estos punteros se guardan en el ejemplar CExpertAdvisors. El ejemplar CExpertAdvisors (el contenedor, no el ejemplar del asesor) iniciliaza a continuación sus componentes y, en caso necesario, carga los datos anteriores.

El asesor usa la función definida por el usuario para crear el ejemplar СExpertAdvisor. Más abajo se muestra la función usada para crear el ejemplar del cuarto asesor:

CExpertAdvisor *expert_custom_trail_ha_ma()
{
   CExpertAdvisor *expert=new CExpertAdvisor();
   expert.Init(Symbol(),Period(),magic4,true,true,true);
   CMoneys *money_manager=new CMoneys();
   CMoney *money_fixed=new CMoneyFixedLot(0.05);
   CMoney *money_ff=new CMoneyFixedFractional(5);
   CMoney *money_ratio=new CMoneyFixedRatio(0,0.1,1000);
   CMoney *money_riskperpoint=new CMoneyFixedRiskPerPoint(0.1);
   CMoney *money_risk=new CMoneyFixedRisk(100);

   money_manager.Add(money_fixed);
   money_manager.Add(money_ff);
   money_manager.Add(money_ratio);
   money_manager.Add(money_riskperpoint);
   money_manager.Add(money_risk);
   expert.AddMoneys(GetPointer(money_manager));

   CTimes *time_filters=new CTimes();
   if(time_range_enabled && time_range_end>0 && time_range_end>time_range_start)
     {
      CTimeRange *timerange=new CTimeRange(time_range_start,time_range_end);
      time_filters.Add(GetPointer(timerange));
     }
   if(time_days_enabled)
     {
      CTimeDays *timedays=new CTimeDays(sunday_enabled,monday_enabled,tuesday_enabled,wednesday_enabled,thursday_enabled,friday_enabled,saturday_enabled);
      time_filters.Add(GetPointer(timedays));
     }
   if(timer_enabled)
     {
      CTimer *timer=new CTimer(timer_minutes*60);
      timer.TimeStart(TimeCurrent());
      time_filters.Add(GetPointer(timer));
     }

   switch(time_intraday_set)
     {
      case INTRADAY_SET_1:
        {
         CTimeFilter *timefilter=new CTimeFilter(time_intraday_gmt,intraday1_hour_start,intraday1_hour_end,intraday1_minute_start,intraday1_minute_end);
         time_filters.Add(timefilter);
         break;
        }
      case INTRADAY_SET_2:
        {
         CTimeFilter *timefilter=new CTimeFilter(0,0,0);
         timefilter.Reverse(true);
         CTimeFilter *sub1 = new CTimeFilter(time_intraday_gmt,intraday2_hour1_start,intraday2_hour1_end,intraday2_minute1_start,intraday2_minute1_end);
         CTimeFilter *sub2 = new CTimeFilter(time_intraday_gmt,intraday2_hour2_start,intraday2_hour2_end,intraday2_minute2_start,intraday2_minute2_end);
         timefilter.AddFilter(sub1);
         timefilter.AddFilter(sub2);
         time_filters.Add(timefilter);
         break;
        }
      default: break;
     }
   expert.AddTimes(GetPointer(time_filters));

   CStops *stops=new CStops();
   CCustomStop *main=new CCustomStop("main");
   main.StopType(stop_type_main);
   main.VolumeType(VOLUME_TYPE_PERCENT_TOTAL);
   main.Main(true);
//main.StopLoss(stop_loss);
//main.TakeProfit(take_profit);
   stops.Add(GetPointer(main));

   CTrails *trails=new CTrails();
   CCustomTrail *trail=new CCustomTrail();
   trails.Add(trail);
   main.Add(trails);

   expert.AddStops(GetPointer(stops));

   MqlParam params[1];
   params[0].type=TYPE_STRING;
#ifdef __MQL5__
   params[0].string_value="Examples\\Heiken_Ashi";
#else
   params[0].string_value="Heiken Ashi";
#endif
   SignalHA *signal_ha=new SignalHA(Symbol(),0,1,params,signal_bar);
   SignalMA *signal_ma=new SignalMA(Symbol(),(ENUM_TIMEFRAMES) Period(),maperiod,0,mamethod,maapplied,signal_bar);
   CSignals *signals=new CSignals();
   signals.Add(GetPointer(signal_ha));
   signals.Add(GetPointer(signal_ma));
   expert.AddSignal(GetPointer(signals));
//--- 
   return expert;
}

Como podemos ver, el código se parace mucho a la función OnInit del archivo de encabezamiento del asesor original (expert_custom_trail_ha_ma.mqh). Otras funciones definidas por el usuario se organizan de forma semejante.

Observaciones finales

Antes de concluir esta serie de artículos, voy a hablar un poco a los lectores que quieran utilizar la biblioteca acerca de los factores que contribuyen a su desarrollo.

Hasta la fecha, la biblioteca que se presenta en el anexo a este artículo tiene más de 10.000 líneas de código (incluyendo los comentarios). A pesar de esto, todavía no se ha completado. Para sacar el máximo provecho de MQL4 y MQL5, aún hay queda mucho trabajo.

Empecé a trabajar en este proyecto antes de que en MetaTrader 5 estuviese disponible el modo de cobertura. Esto influyó mucho en el desarrollo posterior de la biblioteca. Como resultado, la biblioteca tiende a adoptar las convenciones usadas en MetaTrader 4, más que las de MetaTrader 5. Además, me he encontrado con una serie de problemas de compatibilidad en algunas versiones lanzadas en los últimos años, lo que ha implicado la introducción de ciertos ajustes en el código (y como consecuencia de ello, se ha retrasado la publicación de algunas partes de esta serie). Al momento de escribir estas líneas, el lanzamiento de actualizaciones para ambas plataformas se ha convertido en algo cada vez más raro y estable. Se espera que esta tendencia aumente. Sin embargo, la biblioteca deberá perfeccionarse para futuras actualizaciones de las plataformas que podrían causar incompatibilidad.

La biblioteca utiliza los datos almacenados en la memoria para monitorear sus propias operaciones. Esto se ha convertido en la razón por la que un experto creado con su uso, depende en gran medida del almacenamiento y carga de los datos para permitir la recuperación del trabajo después de posibles interrupciones y fallos. El posterior trabajo sobre esta biblioteca (así como sobre cualquier producto multiplataforma) deberá estar orientado a la implementación independiente (o casi independiente), como la implementación de la Biblioteca Estándar MQL5.

Y una última observación: la biblioteca creada en esta serie de artículos no debe ser considerada como una solución permanente. Se la puede utilizar para una transición más suave de MetaTrader 4 a MetaTrader 5. La incompatibilidad entre MQL4 y MQL5 puede ser un serio obstáculo para los tráders que tengan la intención de pasar a la nueva plataforma. Como resultado, el código fuente en MQL4 escrito para sus asesores debe ser rediseñado para que sea compatible con el compilador MQL5. La biblioteca creada en este artículo se presenta como un medio para desplegar el asesor en una nueva plataforma con algunos ajustes en el código fuente, o ninguno en absoluto. Esto puede ayudar al tráder a la hora de decidir si va a seguir utilizando MetaTrader 4 o va a dar el salto a MetaTrader 5. Si finalmente decide cambiar, el uso de esta biblioteca puede reducir al mínimo el número de cambios, permitiendo que el tráder pueda utilizar el asesor de la forma acostumbrada. Pero, en el caso de seguir usando por el momento la plataforma antigua, el tráder tendrá todavía la oportunidad de cambiar rápidamente a MetaTrader 5 en el momento en el que los desarrolladores dejen de dar soporte a MetaTrader 4.

Conclusión

En este artículo se han desarrollado los objetos de la clase CExpertAdvisor y CExpertAdvisors, que se usan para integrar todos los componentes del asesor comercial multiplataforma analizados en los anteriores artículos. El artículo estudia cómo se crean estas dos clases y cómo se relacionan con los demás componentes del asesor comercial multiplataforma. Asimismo, se presentan varias soluciones a los problemas que surgen habitualmente al trabajar con asesores: la detección de una nueva barra, y el guardado y la carga de los datos modificados.

Programas utilizados en el artículo

 # Nombre
Tipo
Descripción de los parámetros
1
expert_breakeven_ha_ma.mqh
Archivo de encabezamiento
Archivo de encabezamiento principal utilizado en el primer ejemplo
2.
expert_breakeven_ha_ma.mq4 Asesor Experto
Archivo fuente principal utilizado para la versión MQL4 en el primer ejemplo
3.
expert_breakeven_ha_ma.mq5 Asesor Experto Archivo fuente principal utilizado para la versión MQL5 en el primer ejemplo
4.
 expert_trail_ha_ma.mqh Archivo de encabezamiento Archivo de encabezamiento principal utilizado en el segundo ejemplo
5.
 expert_trail_ha_ma.mq4 Asesor Experto Archivo fuente principal utilizado para la versión MQL4 en el segundo ejemplo
6.
 expert_trail_ha_ma.mq5 Asesor Experto Archivo fuente principal utilizado para la versión MQL5 en el segundo ejemplo
7.
 expert_custom_stop_ha_ma.mqh Archivo de encabezamiento Archivo de encabezamiento principal utilizado en el tercer ejemplo
8.
 expert_custom_stop_ha_ma.mq4 Asesor Experto Archivo fuente principal utilizado para la versión MQL4 en el tercer ejemplo
9.
 expert_custom_stop_ha_ma.mq5 Asesor Experto Archivo fuente principal utilizado para la versión MQL5 en el tercer ejemplo
10.
 expert_custom_trail_ha_ma.mqh Archivo de encabezamiento Archivo de encabezamiento principal utilizado en el cuarto ejemplo
11.
 expert_custom_trail_ha_ma.mq4 Asesor Experto Archivo fuente principal utilizado para la versión MQL4 en el cuarto ejemplo
12.
 expert_custom_trail_ha_ma.mq5 Asesor Experto Archivo fuente principal utilizado para la versión MQL5 en el cuarto ejemplo
13.
 combined.mqh Archivo de encabezamiento Archivo de encabezamiento principal utilizado en el quinto ejemplo
13.
 combined.mq4 Asesor Experto Archivo fuente principal utilizado para la versión MQL4 en el quinto ejemplo
15.
 combined.mq5 Asesor Experto Archivo fuente principal utilizado para la versión MQL5 en el quinto ejemplo

Archivos de las clases creadas en el artículo

#
Nombre
Tipo
Descripción de los parámetros
1 MQLx\Base\Expert\ExperAdvisorsBase Archivo de encabezamiento
CExpertAdvisors (contenedor CExpertAdvisor, clase básica)
2.
MQLx\MQL4\Expert\ExperAdvisors Archivo de encabezamiento CExpertAdvisors (versión MQL4)
3.
MQLx\MQL5\Expert\ExperAdvisors Archivo de encabezamiento
CExpertAdvisors (versión MQL5)
4.
MQLx\Base\Expert\ExperAdvisorBase Archivo de encabezamiento
CExpertAdvisor (clase básica)
5.
MQLx\MQL4\Expert\ExperAdvisor Archivo de encabezamiento
CExpertAdvisor (versión MQL4)
6.
MQLx\MQL5\Expert\ExperAdvisor Archivo de encabezamiento
CExpertAdvisor (versión MQL5)
7.
MQLx\Base\Candle\CandleManagerBase Archivo de encabezamiento CCandleManager (contenedor CCandle, clase básica)
8.
MQLx\MQL4\Candle\CandleManager Archivo de encabezamiento CCandleManager (versión MQL4)
9.
MQLx\MQL5\Candle\CandleManager Archivo de encabezamiento CCandleManager (versión MQL5)
10.
MQLx\Base\Candle\CandleBase Archivo de encabezamiento CCandle (clase básica)
11.
MQLx\MQL4\Candle\Candle Archivo de encabezamiento CCandle (versión MQL4)
12.
MQLx\MQL5\Candle\Candle Archivo de encabezamiento
CCandle (versión MQL5)
13.
MQLx\Base\File\ExpertFileBase Archivo de encabezamiento CExpertFile (clase básica)
13.
MQLx\MQL4\File\ExpertFile Archivo de encabezamiento CExpertFile(versión MQL4)
15.
MQLx\MQL5\File\ExpertFile Archivo de encabezamiento CExpertFile(versión MQL5)


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/3622

Archivos adjuntos |
MQL5.zip (143.3 KB)
Colocando las entradas por los indicadores Colocando las entradas por los indicadores
En la vida de cada trader pueden ocurrir diferentes situaciones. A menudo usamos el historial de transacciones rentables para intentar restablecer una estrategia, y usando el historial de pérdidas, tratamos de mejorarla. En ambos casos comparamos las transacciones con indicadores conocidos. En este artículo, se propone la técnica de comparación por lotes de las transacciones con una serie de indicadores.
R cuadrado como evaluación de la calidad de la curva del balance de la estrategia R cuadrado como evaluación de la calidad de la curva del balance de la estrategia
En este artículo se describe cómo construir el criterio personalizado de la optimización de R². Usando este criterio se puede evaluar la calidad de la curva del balance de la estrategia y eligir las estrategias más estables y crecientes regularmente. Se describen los principios de su construcción, así como los métodos estadísticos que se usan para evaluar las propiedades y la calidad de esta métrica.
Mini emulador del mercado o Probador de estrategias manual Mini emulador del mercado o Probador de estrategias manual
El mini emulador del mercado es un indicador que sirve para la emulación parcial del trabajo en el terminal. Supuestamente, se puede usarlo para la simulación de las estrategias «manuales» del análisis y el trading en el mercado.
Neuroredes profundas (Parte IV). Creación, entrenamiento y simulación de un modelo de neurored Neuroredes profundas (Parte IV). Creación, entrenamiento y simulación de un modelo de neurored
En el artículo se analizan las nuevas posibilidades del paquete darch (v.0.12.0). Se describen los resultados del entrenamiento de una red neuronal profunda con diferentes tipos de datos, estructura y secuencia de entrenamiento. También se analizan los resultados.