Sistemas de Trading Adaptables y su Uso en el Terminal de Cliente MetaTrader 5

MetaQuotes | 20 diciembre, 2013


Introducción

Cientos de miles de traders en todo el mundo usan las plataformas de trading desarrolladas por MetaQuotes Software Corp. El factor clave de su éxito es la superioridad tecnológica basada en años de experiencia y las mejores soluciones de software.

Mucha gente ya ha calculado nuevas oportunidades que ahora están disponibles con el nuevo lenguaje MQL5. Sus elementos principales son un alto rendimiento y la posibilidad de usar programación orientada al objeto. Además, con la aparición del Probador de Estrategias multidivisa en el terminal de cliente MetaTrader 5, muchos traders han adquirido herramientas únicas para desarrollar, aprender y usar sistemas de trading complejos.

El Automated Trading Championship 2010 (Campeonato de Trading Automatizado 2010) comienza este otoño; en él participarán miles de robots de trading creados con MQL5. Ganará el Asesor Experto que consiga el máximo beneficio durante la competición. ¿Pero qué estrategia resultará más efectiva?

El Probador de Estrategias del terminal MetaTrader 5 permite encontrar el mejor conjunto de parámetros, usando aquellos con los que el sistema gana la mayor cantidad de beneficios durante un período de tiempo determinado. ¿Pero se puede hacer en tiempo real? La idea del trading virtual usando varias estrategias en un Asesor Experto se trata en el artículo "Concurso de Asesores Expertos dentro de un Asesor Experto", que contiene su implementación en MQL4. 

En este artículo, mostraremos que la creación y análisis de estrategias adaptables se ha facilitado significativamente en MQL5 a causa del uso de programación orientada al objeto, clases para trabajar con datos y clases de trading de la Biblioteca estándar.


1. Estrategias de Trading Adaptables

Los mercados cambian constantemente. Las estrategias de trading deben adaptarse a las condiciones de mercado actuales.

Los valores de parámetros que dan el máximo beneficio de la estrategia se pueden encontrar sin usar la optimización a través de un cambio secuencial de parámetros y análisis de resultados de simulaciones. 

La figura 1 demuestra los gráficos de beneficio de 10 Asesores Expertos (MA_3,...MA_93); cada uno de ellos calculado con la estrategia de medias móviles pero con diferentes períodos (3,13,..93). Las simulaciones se han realizado con EURUSD H1, el período de prueba es del 4/01/2010 al 20/08/2010.

Figura 1. Diagramas de gráficos de beneficios de 10 Expert Advisors en la cuenta

Figura 1. Diagramas de gráficos de beneficios de 10 Asesores Expertos en la cuenta

Como puede ver en la figura 1,  los Asesores Expertos tienen aproximadamente los mismos resultados durante las primeras dos semanas, pero a partir de ahí sus niveles de rentabilidad comienzan a diferenciarse significativamente. Al final del período de prueba, los mejores resultados de trading se muestran en los Asesores Expertos con períodos 63, 53 y 43.

El mercado ha elegido los mejores. ¿Por qué no deberíamos seguir su elección? ¿Qué pasaría si combináramos las 10 estrategias en un solo Asesor Experto, facilitáramos la posibilidad de un trading "virtual" para cada estrategia, y determináramos periódicamente (por ejemplo, al comienzo de cada nueva barra) la mejor estrategia para el trading real y el trading de acuerdo con sus señales?

Los resultados obtenidos en la estrategia adaptable se muestran en la figura 2. El gráfico de beneficios de la cuenta con trading adaptable se muestra en color rojo. Note que durante más de medio período la forma del gráfico de beneficios para la estrategia adaptable es la misma que la de la estrategia MA_63, que al parecer fue la ganadora al final.

Figura 2. Gráficos de beneficios en la cuenta con la estrategia adaptable que usa señales de 10 sistemas de trading.

Figura 2. Gráficos de beneficios en la cuenta con la estrategia adaptable que usa señales de 10 sistemas de trading.

Los gráficos de saldo tienen dinámicas similares (Fig. 3):

Figura 3. Gráficos de saldo de la estrategia adaptable que usa señales de 10 sistemas de trading.

Figura 3. Gráficos de saldo de la estrategia adaptable que usa señales de 10 sistemas de trading.

Si ninguna de las estrategias resulta rentable actualmente, el sistema adaptable no debería realizar operaciones de trading. Un ejemplo de tal caso se muestra en la fig. 4 (período del 4 al 22 de enero de 2010).

Figura 4. El período en el que la estrategia adaptable dejó de abrir nuevas posiciones a causa de la falta de estrategias rentables

Figura 4. El período en el que la estrategia adaptable dejó de abrir nuevas posiciones a causa de la falta de estrategias rentables

Empezando en enero de 2010, la mayor efectividad se observa en la estrategia MA_3. Puesto que el MA_3 (azul) ganó la máxima cantidad de dinero en ese momento, la estrategia adaptable (roja) siguió sus señales. En el período del 8 al 20 de enero, todas las estrategias consideradas dieron un resultado negativo, por eso la estrategia adaptable no abrió ninguna posición de trading nueva. 

Si todas las estrategias tienen un resultado negativo, es mejor abstenerse de realizar operaciones de trading. Con esto, evitaremos negocios no rentables y mantendremos nuestro dinero seguro.


2. Implementación de la Estrategia de Trading Adaptable

En esta sección, trataremos la estructura de la estrategia adaptable, que realiza el trading "virtual" usando varias estrategias de trading al mismo tiempo y elige la más rentable para el trading real de acuerdo con sus señales. Note que el uso del enfoque orientado al objeto facilita considerablemente la solución de este problema.

En primer lugar, investigaremos el código del Asesor Experto adaptable. Después, examinaremos en detalle la clase CAdaptiveStrategy, en la que se implementa la funcionalidad de los sistemas adaptables. Finalmente, mostraremos la estructura de la clase CSampleStrategy, la clase base de las estrategias de trading en las que se implementa la funcionalidad del trading virtual.

A continuación, trataremos el código de dos de sus descendientes: las clases CStrategyMA y CStrategyStoch, que representan las estrategias de trading de medias móviles y el oscilador estocástico. Tras analizar su estructura, podrá escribir y añadir sus propias clases para realizar sus estrategias fácilmente.

2.1. Código del Asesor Experto

El código del Asesor Experto tiene un aspecto muy sencillo:

//|                                       Adaptive_Expert_Sample.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <CAdaptiveStrategy.mqh>

CAdaptiveStrategy Adaptive_Expert;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(Adaptive_Expert.Expert_OnInit());
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Adaptive_Expert.Expert_OnDeInit(reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Adaptive_Expert.Expert_OnTick();
  }
//+------------------------------------------------------------------+

Las primeras tres líneas definen las propiedades del programa. Luego viene el comando #include directive que indica al preprocesador que incluya el archivo CAdaptiveStrategy.mqh. Los paréntesis angulares especifican que el archivo debe tomarse del directorio estándar (habitualmente, es terminal_folder\MQL5\Include).

La siguiente línea contiene la declaración del objeto Adaptive_Expert (instancia de la clase CAdaptiveStrategy). Asimismo, el código de las funciones OnInit, OnDeinit y OnTick del Asesor Experto consta de las llamadas de las funciones correspondientes Expert_OnInit,  Expert_OnDeInit y Expert_OnTick, y el objeto Adaptive_Expert.

2.2. La clase CAdaptiveStrategy

La clase del Asesor Experto adaptable (clase CAdaptiveStrategy) se encuentra en el archivo CAdaptiveStrategy.mqh. Comencemos con los archivos que incluye:

#include <Arrays\ArrayObj.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <CStrategyMA.mqh>
#include <CStrategyStoch.mqh>

La razón por la que incluimos el archivo ArrayObj.mqh es la conveniencia de trabajar con clases de diferentes estrategias usando el objeto de la clase CArrayObj, que representa un array dinámico de instrucciones para las instancias de clase generadas por la clase base CObject y sus descendientes. El array m_all_strategies se usará como "contenedor" de estrategias de trading.

Cada estrategia se representa como una clase. En este caso, hemos incluido los archivos que contienen las clases CStrategyMA y CStrategyStoch, que representan las estrategias de trading con medias móviles y trading con el oscilador estocástico.

Para solicitar propiedades de posiciones actuales y para realizar operaciones de trading, usaremos las clases CPositionInfo y CTrade de la Biblioteca estándar. Por ello hemos incluido los archivos PositionInfo.mqh y Trade.mqh.

Echemos un vistazo a la estructura de la clase CAdaptiveStrategy.

//+------------------------------------------------------------------+
//| Class CAdaptiveStrategy                                          |
//+------------------------------------------------------------------+
class CAdaptiveStrategy
  {
protected:
   CArrayObj        *m_all_strategies;   // objects of trade strategies

   void              ProceedSignalReal(int state,double trade_volume);
   int               RealPositionDirection();

public:
   // initialization of the adaptive strategy
   int               Expert_OnInit();
   // deinitialization of the adaptive strategy
   int               Expert_OnDeInit(const int reason);
   // check of trade conditions and opening of virtual positions
   void              Expert_OnTick();
  };

Para implementar un efoque unitario a los objetos de diferentes clases, las estrategias de trading (o, mejor dicho, las instancias de sus clases) se almacenan en el array dinámico m_all_strategies (del tipo CArrayObj), que se usa como "contenedor" de clases de las estrategias. Esta es la razón por la cual la clase de estrategias de trading SampleStrategy se genera en la clase CObject.

La función ProceedSignalReal implementa la "sincronización" de la dirección y volumen de una posición real con la dirección y volumen indicados:

//+------------------------------------------------------------------+
//| This method is intended for "synchronization" of current         |
//| real trade position with the value of the 'state' state          |
//+------------------------------------------------------------------+
void CAdaptiveStrategy::ProceedSignalReal(int state,double trade_volume)
  {
   CPositionInfo posinfo;
   CTrade trade;

   bool buy_opened=false;
   bool sell_opened=false;

   if(posinfo.Select(_Symbol)) // if there are open positions
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) buy_opened=true;    // a buy position is opened
      if(posinfo.Type()==POSITION_TYPE_SELL) sell_opened=true;  // a sell position is opened

      // if state = 0, then we need to close open positions
      if((state==POSITION_NEUTRAL) && (buy_opened || sell_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
        }
      //reverse: closing buy position and opening sell position
      if((state==POSITION_SHORT) && (buy_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      //reverse: close sell position and open buy position
      if(((state==POSITION_LONG) && (sell_opened)))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
   else // if there are no open positions
     {
      // open a buy position
      if(state==POSITION_LONG)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.1,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      // open a sell position
      if(state==POSITION_SHORT)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,0.1,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
  }

Note que es más fácil trabajar con la posición de trading usando las clases de trading. Utilizamos los objetos de las clases CPositionInfo y CTrade para la solicitud de propiedades de posición de mercado y para realizar operaciones de trading respectivamente.

La función RealPositionDirection solicita los parámetros de la posición abierta real y devuelve su dirección:

//+------------------------------------------------------------------+
//| Returns direction (0,+1,-1) of the current real position         |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::RealPositionDirection()
  {
   int direction=POSITION_NEUTRAL;
   CPositionInfo posinfo;

   if(posinfo.Select(_Symbol)) // if there are open positions
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) direction=POSITION_LONG;    // a buy position is opened
      if(posinfo.Type()==POSITION_TYPE_SELL) direction=POSITION_SHORT;  // a short position is opened
     }
   return(direction);
  }

Ahora echaremos un vistazo a las funciones principales de la clase СAdaptiveStrategy.

Empecemos con la función Expert_OnInit:

//+------------------------------------------------------------------+
//| Function of initialization of the Adaptive Expert Advisor        |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnInit()
  {
//--- Create array of objects m_all_strategies
//--- we will put our object with strategies in it 
   m_all_strategies=new CArrayObj;
   if(m_all_strategies==NULL)
     {
      Print("Error of creation of the object m_all_strategies"); return(-1);
     }

// create 10 trading strategies CStrategyMA (trading by moving averages)
// initialize them, set parameters
// and add to the m_all_strategies container 
   for(int i=0; i<10; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialize strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //t_StrategyMA.Set_Stops(3500,1000);

      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_SampleStrategy;
      t_SampleStrategy=m_all_strategies.At(i);
      Print(i," Strategy name:",t_SampleStrategy.StrategyName(),
              " Strategy ID:",t_SampleStrategy.StrategyID(),
              " Virtual trading:",t_SampleStrategy.IsVirtualTradeAllowed());
     }
//---
   return(0);
  }

El establecimiento de estrategias de trading se prepara en la función Expert_OnInit. En primer lugar, se crea el objeto del array dinámico m_all_strategies.

En este caso, creamos diez instancias de la clase CStrategyMA. Cada una de ellas se inicializó (en este caso, establecimos diferentes períodos y permitimos el trading "virtual") usando la función Inicialización.

A continuación, utilizando la función SetStrategyInfo, establecimos el instrumento financiero, nombre de estrategia y comentario.

Si es necesario, podemos especificar un valor (en puntos) de Take Profit y Stop Loss usando la función Set_Stops(TP,SL), que se ejecutará durante el trading "virtual". Tenemos esta línea comentada.

Una vez que la estrategia se ha creado y ajustado, la añadimos al contenedor m_all_strategies.

Todas las clases de estrategias de trading deberían tener la función CheckTradeConditions(), que revisa las condiciones comerciales. En la clase de la estrategia adaptable, esta función se llama al principio de cada barra nueva, y con ello damos a las estrategias la posibilidad de comprobar los valores de los indicadores y de hacer las operaciones de trading "virtual".

En lugar de diez medias móviles especificadas (3, 13, 23...93), podemos añadir cientos de medias móviles (instancias de la clase CStrategyMA):

  for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialization of strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

O podemos añadir las clases de estrategia que funcionan por la señal del oscilador estocástico (instancias de la clase CStrategyStoch):

    for(int i=0; i<5; i++)
     {
      CStrategyStoch *t_StrategyStoch;
      t_StrategyStoch=new CStrategyStoch;
      if(t_StrategyStoch==NULL)
        {
         delete m_all_strategies;
         printf("Error of creation of object of the CStrategyStoch type");
         return(-1);
        }
      //set period for each strategy
      int Kperiod=2+i*5;
      int Dperiod=2+i*5;
      int Slowing=3+i;
      // initialization of strategy
      t_StrategyStoch.Initialization(Kperiod,Dperiod,Slowing,true);
      // set details of the strategy
      string s=IntegerToString(Kperiod)+"/"+IntegerToString(Dperiod)+"/"+IntegerToString(Slowing);
      t_StrategyStoch.SetStrategyInfo(_Symbol,"[Stoch_"+s+"]",100+i," Stochastic "+s);
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyStoch);
     }

En este caso, el contenedor incluye 10 estrategias de medias móviles y 5 estrategias del oscilador estocástico.

Las instancias de clases de estrategias de trading deberían ser los descendientes de la clase CObject, y deberían contener la función CheckTradeConditions(). Es mejor heredarlas de la clase CSampleStrategy. Las clases que implementan estrategias de trading pueden ser diferentes, y sus números no están limitados.

La función Expert_OnInit termina con la lista de estrategias presentes en el contenedor m_all_strategies. Note que todas las estrategias en el contenedor no se consideran los descendientes de la clase CSampleStrategy. Las clases de las estrategias de trading CStrategyMA y CStrategyStoch también son sus descendientes.

El mismo truco se usa en la función Expert_OnDeInit. En el contenedor, llamamos a la función SaveVirtualDeals para cada estrategia; almacena el historial de las transacciones virtuales llevadas a cabo.

Usamos el nombre de estrategia para el nombre de archivo que se pasa como parámetro. Después, desinicializamos las estrategias llamando a la función Deinitialization() y eliminando el contenedor m_all_strategies:

//+------------------------------------------------------------------+
//| Function of deinitialization the adaptive Expert Advisor         |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnDeInit(const int reason)
  {
   // deinitialize all strategies
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_Strategy;
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.SaveVirtualDeals(t_Strategy.StrategyName()+"_deals.txt");
      t_Strategy.Deinitialization();
     }
   //delete the array of object with strategies 
   delete m_all_strategies;
   return(0);
  }

Si no necesita conocer las transacciones virtuales llevadas a cabo por las estrategias, elimine la línea donde se llama a la función tStrategy.SaveVirtualDeals. Note que cuando se usa el Probador de Estrategias, los archivos se guardan en el directorio /tester_directory/Files/.

Veamos la función Expert_OnTick de la clase CAdaptiveStrategy, a la que se llama cada vez que viene un nuevo tick:

//+---------------------------------------------------------------------------------------+
//| Function of processing ticks of the adaptive strategy                                 |
//+---------------------------------------------------------------------------------------+
void CAdaptiveStrategy::Expert_OnTick()
  {
   CSampleStrategy *t_Strategy;

   // recalculate the information about positions for all strategies
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.UpdatePositionData();
     }

   // the expert advisor should check the conditions of making trade operations only when a new bar comes
   if(IsNewBar()==false) { return; }

   // check trading conditions for all strategies 
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.CheckTradeConditions();
     }

   //search for the best position
   //prepare the array Performance[] 
   double performance[];
   ArrayResize(performance,m_all_strategies.Total());
   
   //request the current effectiveness for each strategy,
   //each strategy returns it in the Strategyperformance() function
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      performance[i]=t_Strategy.StrategyPerformance();
     }
   //find the strategy (or rather its index in the m_all_strategies container)
   //with maximum value of Strategyperformance()
   int best_strategy_index=ArrayMaximum(performance,0,WHOLE_ARRAY);

   //this strategy is - t_Strategy
   t_Strategy=m_all_strategies.At(best_strategy_index);
   //request the direction of its current position
   int best_direction=t_Strategy.PositionDirection();

   string s=s+" "+t_Strategy.StrategyName()+" "+DoubleToString(t_Strategy.GetVirtualEquity())+" "+IntegerToString(best_direction);
   Print(TimeCurrent()," TOTAL=",m_all_strategies.Total(),
                       " BEST IND=",best_strategy_index,
                       " BEST STRATEGY="," ",t_Strategy.StrategyName(),
                       " BEST=",performance[best_strategy_index],"  =",
                       " BEST DIRECTION=",best_direction,
                       " Performance=",t_Strategy.StrategyPerformance());

   //if the best strategy has a negative result and doesn't have open positions, it's better to stay away from trading
   if((performance[best_strategy_index]<0) && (RealPositionDirection()==POSITION_NEUTRAL)) {return;}

   if(best_direction!=RealPositionDirection())
     {
      ProceedSignalReal(best_direction,t_Strategy.GetCurrentLotSize());
     }
  }

El código es muy sencillo. Cada estrategia localizada en el contenedor debe ser capaz de recalcular el resultado financiero actual de sus posiciones virtuales usando los precios actuales. Se hace llamando a la función UpdatePositionData(). Aquí, de nuevo podemos considerar a las estrategias como las herederas de la clase CSampleStrategy.

Todas las operaciones comerciales se realizan en el comienzo de una nueva barra (la función IsNewBar() permite determinar este momento, así como los otros métodos de revisar una nueva barra). En este caso, el final de la formación de una barra significa que todos los datos de la barra anterior (precios y valores del indicador) no cambiarán más, de modo que puede ser analizada en correspondencia con las condiciones de trading. Llamando a su función CheckTradeConditions, le damos la oportunidad a todas las estrategias de realizar esta prueba y realizar sus operaciones de trading virtuales.

Ahora debemos encontrar la estrategia con más éxito entre todas las estrategias en el array m_all_strategies. Para ello, utilizamos el array Performance[], en el que se colocan los valores devueltos con la función StrategyPerformance() de cada estrategia. La clase base CSampleStrategy contiene esta función como la diferencia entre los valores actuales de Beneficio y Saldo "virtuales".

La búsqueda del índice de la estrategia con menos éxito se hace usando la función ArrayMaximum. Si la mejor estrategia tiene una rentabilidad negativa en ese momento y no tiene posiciones abiertas, es mejor abstenerse de hacer operaciones de trading, por eso saldremos de la función (vea la sección 1).

Además, solicitamos la dirección de la posición virtual de esta estrategia (best_direction). Si es diferente de la dirección actual de la posición real, entonces la dirección actual de la posición real se corregirá (usando la función ProceedSignalReal) de acuerdo con la dirección best_direction.

2.3. Clase CSampleStrategy

Las estrategias situadas en el contenedor m_all_strategies se han considerado como herederas de la clase CSampleStrategy.

Esta clase es la primera base para las estrategias de trading: contiene la implementación de trading virtual. En este artículo, trataremos un caso simplificado de implementación de trading virtual en el que los cambios no se tienen en cuenta. Las clases de estrategias de trading deberían heredarse de la clase CSampleStrategy.

Esta es la estructura de esta clase.

//+------------------------------------------------------------------+                                              CSampleStrategy.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Object.mqh>

#define POSITION_NEUTRAL   0     // no position
#define POSITION_LONG      1     // long position
#define POSITION_SHORT    -1     // short position

#define SIGNAL_OPEN_LONG    10   // signal to open a long position
#define SIGNAL_OPEN_SHORT  -10   // signal to open a short position
#define SIGNAL_CLOSE_LONG   -1   // signal to close a long position
#define SIGNAL_CLOSE_SHORT   1   // signal to close a short position
//+------------------------------------------------------------------+
//| Structure for storing the parameters of virtual position         |
//+------------------------------------------------------------------+
struct virtual_position
  {
   string            symbol;            // symbol
   int               direction;         // direction of the virtual position (0-no open position,+1 long,-1 short)
   double            volume;            // volume of the position in lots
   double            profit;            // current profit of the virtual position on points
   double            stop_loss;         // Stop Loss of the virtual position
   double            take_profit;       // Take Profit of the virtual position
   datetime          time_open;         // date and time of opening the virtual position
   datetime          time_close;        // date and time of closing the virtual position
   double            price_open;        // open price of the virtual position
   double            price_close;       // close price of the virtual position
   double            price_highest;     // maximum price during the life of the position
   double            price_lowest;      // minimal price during the lift of the position
   double            entry_eff;         // effectiveness of entering
   double            exit_eff;          // effectiveness of exiting
   double            trade_eff;         // effectiveness of deal
  };
//+------------------------------------------------------------------+
//| Class CSampleStrategy                                            |
//+------------------------------------------------------------------+
class CSampleStrategy: public CObject
  {
protected:
   int               m_strategy_id;            // Strategy ID
   string            m_strategy_symbol;        // Symbol 
   string            m_strategy_name;          // Strategy name
   string            m_strategy_comment;       // Comment

   MqlTick           m_price_last;             // Last price
   MqlRates          m_rates[];                // Array for current quotes
   bool              m_virtual_trade_allowed;  // Flag of allowing virtual trading 
   int               m_current_signal_state;   // Current state of strategy
   double            m_current_trade_volume;   // Number of lots for trading
   double            m_initial_balance;        // Initial balance (set in the constructor, default value is 10000)
   int               m_sl_points;              // Stop Loss
   int               m_tp_points;              // Take Profit

   virtual_position  m_position;               // Virtual position
   virtual_position  m_deals_history[];        // Array of deals
   int               m_virtual_deals_total;    // Total number of deals

   double            m_virtual_balance;           // "Virtual" balance
   double            m_virtual_equity;            // "Virtual" equity
   double            m_virtual_cumulative_profit; // cumulative "virtual" profit
   double            m_virtual_profit;            // profit of the current open "virtual" position

   //checks and closes the virtual position by stop levels if it is necessary
   bool              CheckVirtual_Stops(virtual_position &position);
   // recalculation of position and balance
   void              RecalcPositionProperties(virtual_position &position);
   // recalculation of open virtual position in accordance with the current prices 
   void              Position_RefreshInfo(virtual_position &position);
   // open virtual short position
   void              Position_OpenShort(virtual_position &position);
   // closes virtual short position  
   void              Position_CloseShort(virtual_position &position);
   // opens virtual long position
   void              Position_OpenLong(virtual_position &position);
   // closes the virtual long position
   void              Position_CloseLong(virtual_position &position);
   // closes open virtual position  
   void              Position_CloseOpenedPosition(virtual_position &position);
   // adds closed position to the m_deals_history[] array (history of deals)
   void              AddDealToHistory(virtual_position &position);
   //calculates and returns the recommended volume that will be used in trading
   virtual double    MoneyManagement_CalculateLots(double trade_volume);
public:
   // constructor
   void              CSampleStrategy();
   // destructor
   void             ~CSampleStrategy();

   //returns the current size of virtual balance
   double            GetVirtualBalance() { return(m_virtual_balance); }
   //returns the current size of virtual equity
   double            GetVirtualEquity() { return(m_virtual_equity); }
   //returns the current size of virtual profit of open position
   double            GetVirtualProfit() { return(m_virtual_profit); }

   //sets Stop Loss and Take Profit in points
   void              Set_Stops(int tp,int sl) {m_tp_points=tp; m_sl_points=sl;};
   //sets the current volume in lots
   void              SetLots(double trade_volume) {m_current_trade_volume=trade_volume;};
   //returns the current volume in lots
   double            GetCurrentLots() { return(m_current_trade_volume); }

   // returns strategy name
   string            StrategyName() { return(m_strategy_name); }
   // returns strategy ID
   int               StrategyID() { return(m_strategy_id); }
   // returns the comment of strategy
   string            StrategyComment() { return(m_strategy_comment); }
   // sets the details of strategy (symbol, name and ID of strategy)
   void              SetStrategyInfo(string symbol,string name,int id,string comment);

   // set the flag of virtual trading (allowed or not)
   void              SetVirtualTradeFlag(bool pFlag) { m_virtual_trade_allowed=pFlag; };
   // returns flag of allowing virtual trading
   bool              IsVirtualTradeAllowed() { return(m_virtual_trade_allowed); };

   // returns the current state of strategy
   int               GetSignalState();
   // sets the current state of strategy (changes virtual position if necessary)
   void              SetSignalState(int state);
   // changes virtual position in accordance with the current state 
   void              ProceedSignalState(virtual_position &position);

   // sets the value of cumulative "virtual" profit
   void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_profit; };

   //returns the effectiveness of strategy ()
   double            StrategyPerformance();

   //updates position data
   void              UpdatePositionData();
   //closes open virtual position
   void              CloseVirtualPosition();
   //returns the direction of the current virtual position
   int               PositionDirection();
   //virtual function of initialization
   virtual int       Initialization() {return(0);};
   //virtual function of checking trade conditions
   virtual bool      CheckTradeConditions() {return(false);};
   //virtual function of deinitialization
   virtual int       Deinitialization() {return(0);};

   //saves virtual deals to a file
   void              SaveVirtualDeals(string file_name);
  };

No analizaremos su descripción detallada; podrá encontrar más información en el archivo CSampleStrategy.mqh. Ahí podrá encontrar también la función de revisión de una barra nueva - IsNewBar.


3. Clases de Estrategias de Trading

Esta sección está dedicada a la estructura de clases de estrategias comerciales que se usan en el Asesor Experto adaptable.

3.1. Clase CStrategyMA - Estrategia de Trading con Medias Móviles

//+------------------------------------------------------------------+
//|                                                  CStrategyMA.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Class CStrategyMA for implementation of virtual trading          |
//| by the strategy based on moving average                          |
//+------------------------------------------------------------------+
class CStrategyMA : public CSampleStrategy
  {
protected:
   int               m_handle;     // handle of the Moving Average (iMA) indicator
   int               m_period;     // period of the Moving Average indicator
   double            m_values[];   // array for storing values of the indicator
public:
   // initialization of the strategy
   int               Initialization(int period,bool virtual_trade_flag);
   // deinitialization of the strategy
   int               Deinitialization();
   // checking trading conditions and opening virtual positions
   bool              CheckTradeConditions(); 
}; 

La clase CStrategyMA es descendiente de la clase CSampleStrategy, en la que se implementa toda la funcionalidad del trading virtual.

La sección protegida contiene variables internas que se usarán en la clase de la estrategia. Estas son: m_handle - controlador del indicador iMA, m_period - período de la media móvil, m_values[] - array que se usará en la función CheckTradeConditions para obtener los valores actuales del indicador.

La sección pública contiene tres funciones que facilitan la implementación de la estrategia de trading.

  1. La señal para abrir una posición larga (SIGNAL_OPEN_LONG)
  2. La señal para abrir una posición corta (SIGNAL_OPEN_SHORT)
  3. La señal para cerrar una posición larga (SIGNAL_CLOSE_LONG)
  4. La señal para cerrar una posición corta (SIGNAL_CLOSE_SHORT)
//+------------------------------------------------------------------+
//| Strategy Initialization Method                                   |
//+------------------------------------------------------------------+
int CStrategyMA::Initialization(int period,bool virtual_trade_flag)
  {
   // set period of the moving average
   m_period=period;
   // set specified flag of virtual trading
   SetVirtualTradeFlag(virtual_trade_flag);

   //set indexation of arrays like the one of timeseries
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_values,true);
   
   //create handle of the indicator
   m_handle=iMA(_Symbol,_Period,m_period,0,MODE_EMA,PRICE_CLOSE);
   if(m_handle<0)
     {
      Alert("Error of creation of the MA indicator - error number: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Strategy Deinitialization Method                                 |
//+------------------------------------------------------------------+
int CStrategyMA::Deinitialization()
  {
   Position_CloseOpenedPosition(m_position);
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| Checking trading conditions and opening virtual positions        |
//+------------------------------------------------------------------+
bool CStrategyMA::CheckTradeConditions()
  {
   RecalcPositionProperties(m_position);
   double p_close;

   // get history data of the last three bars
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Error of copying history data - error:",GetLastError(),"!!");
      return(false);
     }
   // Copy the current price of closing of the previous bar (it is bar 1)
   p_close=m_rates[1].close;  // close price of the previous bar          

   if(CopyBuffer(m_handle,0,0,3,m_values)<0)
     {
      Alert("Error of copying buffers of the Moving Average indicator - error number:",GetLastError());
      return(false);
     }

   // buy condition 1: MA rises
   bool buy_condition_1=(m_values[0]>m_values[1]) && (m_values[1]>m_values[2]);
   // buy condition 2: previous price is greater than the MA
   bool buy_condition_2=(p_close>m_values[1]);

   // sell condition 1: // MA falls
   bool sell_condition_1=(m_values[0]<m_values[1]) && (m_values[1]<m_values[2]);
   // sell condition 2: // previous price is lower than the MA   
   bool sell_condition_2=(p_close<m_values[1]);

   int new_state=0;

   if(buy_condition_1  &&  buy_condition_2) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition_1 && sell_condition_2) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition_1 || buy_condition_2)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition_1 || sell_condition_2)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true); 
  };

 

El concepto es sencillo: con la base de los estados y precios del indicador, se determina el tipo de señal (new_state), después se solicita el estado actual del trading virtual (usando la función GetSignalState); y si no son iguales, se llama a la función SetSignalState para "corregir" las posiciones virtuales.

3.2. Clase CStrategyStoch - la Estrategia de Trading con el Oscilador Estocástico

El código de la clase que realiza operaciones de trading con base en la intersección de las líneas principal y de señal del oscilador estocástico iStochastic se muestra a continuación:

//+------------------------------------------------------------------+
//|                                               CStrategyStoch.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Class CStrategyStoch for implementation of virtual trading by    |
//| the strategy of intersection of lines of stochastic oscillator   |
//+------------------------------------------------------------------+
class CStrategyStoch : public CSampleStrategy
  {
protected:
   int               m_handle;          // handle of the Stochastic Oscillator (iStochastic)
   int               m_period_k;        // K-period (number of bars for calculations)
   int               m_period_d;        // D-period (period of primary smoothing)
   int               m_period_slowing;  // final smoothing
   double            m_main_line[];     // array for storing indicator values
   double            m_signal_line[];   // array for storing indicator values
public:
   // initialization of strategy
   int               Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag);
   // deinitialization of strategy
   int               Deinitialization();
   // checking trading conditions and opening virtual positions
   bool              CheckTradeConditions();
  };
//+------------------------------------------------------------------+
//| Strategy Initialization Method                                   |
//+------------------------------------------------------------------+
int CStrategyStoch::Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag)
  {
   // Set period of the oscillator
   m_period_k=period_k;
   m_period_d=period_d;
   m_period_slowing=period_slowing;

   // set specified flag of the virtual trading
   SetVirtualTradeFlag(virtual_trade_flag);

   // set indexation of arrays like the one of timeseries
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_main_line,true);
   ArraySetAsSeries(m_signal_line,true);

   // create handle of the indicator
   m_handle=iStochastic(_Symbol,_Period,m_period_k,m_period_d,m_period_slowing,MODE_SMA,STO_LOWHIGH);
   if(m_handle<0)
     {
      Alert("Error of creating the Stochastic indicator - error number: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Strategy Deinitialization Method                                 |
//+------------------------------------------------------------------+
int CStrategyStoch::Deinitialization()
  {
   // close all open positions
   Position_CloseOpenedPosition(m_position);
   // release handle of the indicator
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| Checking Trading Conditions and Opening Virtual Positions        |
//+------------------------------------------------------------------+
bool CStrategyStoch::CheckTradeConditions()
  {
   // call the functions of recalculation of position parameters
   RecalcPositionProperties(m_position);
   double p_close;

   // get history  data of the last 3 bars 
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Error of copying history data - error:",GetLastError(),"!!");
      return(false);
     }
   // copy the current close price of the previous bar (it is bar 1)
   p_close=m_rates[1].close;  // close price of the previous bar          

   if((CopyBuffer(m_handle,0,0,3,m_main_line)<3) || (CopyBuffer(m_handle,1,0,3,m_signal_line)<3))
     {
      Alert("Error of copying buffers of the Stochastic indicator - error number:",GetLastError());
      return(false);
     }

   // buy condition: crossing the signal line by the main one from bottom up
   bool buy_condition=((m_signal_line[2]<m_main_line[2]) && (m_signal_line[1]>m_main_line[1]));
   // sell condition: crossing the signal line by the main one from top downwards
   bool sell_condition=((m_signal_line[2]>m_main_line[2]) && (m_signal_line[1]<m_main_line[1]));

   int new_state=0;

   if(buy_condition) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true); 
  };


Como puede ver, las únicas diferencias entre la estructura de la clase CStrategyStoch y la de la clase CStrategyMA son la función de inicialización (parámetros diferentes), el tipo de indicador utilizado y las señales de trading.

Por tanto, para usar sus estrategias en el Asesor Experto adaptable, debería reescribirlas en forma de clases de ese tipo y cargarlas en el contenedor m_all_strategies.


4. Resultados de Análisis de las Estrategias de Trading Adaptables

En esta sección, trataremos diversos aspectos del uso práctico de las estrategias adaptables y los métodos para mejorarlas.

4.1. Mejorar el sistema con estrategias que usen señales inversas

Las medias móviles no nos valen cuando no hay tendencias. Ya nos hemos encontrado con este tipo de situación antes: en la figura 3 puede ver que no había tendencia alguna en el periodo del 8 al 20 de enero, de modo que las 10 estrategias que utilizan medias móviles en trading mostraron una pérdida virtual. El sistema adaptable detuvo las operaciones de trading como resultado de la falta de una estrategia con ganancias positivas. ¿Hay alguna forma de evitar tal efecto negativo?

Añadamos a nuestras 10 estrategias (MA_3, MA_13, ... MA_93) otras 10 clases CStrategyMAinv cuyas señales de trading estén invertidas (las condiciones son las mismas, pero SIGNAL_OPEN_LONG/SIGNAL_OPEN_SHORT y SIGNAL_CLOSE_LONG/SIGNAL_CLOSE_SHORT intercambiaron sus lugares). Así, además de diez estrategias de tendencia (instancias de la clase CStrategyMA), tenemos otras tres estrategias de contra-tendencia (instancias de la clase CStrategyMAinv).

El resultado de usar el sistema adaptable con veinte estrategias se muestra en la figura 5.

Figura 5. Diagramas de beneficio en la cuenta de la estrategia adaptable que usa 20 señales de trading: 10 medias móviles CAdaptiveMA y 10 "reflejadas" CAdaptiveMAinv

Figura 5. Diagramas de beneficio en la cuenta de la estrategia adaptable que usa 20 señales de trading: 10 medias móviles CAdaptiveMA y 10 "reflejadas" CAdaptiveMAinv

Como puede ver en la figura 5, durante el período en el que todas las estrategias CAdaptiveMA tenían un resultado negativo, el seguimiento de las estrategias CAdaptiveMAinv permitió al Asesor Experto evitar reducciones no deseadas al comienzo de las operaciones de trading.

Figura 6. Período de tiempo durante el cual la estrategia adaptable usó las señales de las estrategias "contra-tendencia"

Figura 6. Período de tiempo durante el cual la estrategia adaptable usó las señales de las estrategias "contra-tendencia" CAdaptiveMAinv

Este tipo de enfoque puede parecer inaceptable, puesto que perder el depósito es cuestión de tiempo al usar una estrategia de contra-tendencia. No obstante, en nuestro caso no estamos limitados a una sola estrategia. El mercado es el que mejor sabe qué estrategias son más efectivas en ese momento.

Lo mejor de los sistemas adaptables es que el mercado mismo sugiere qué estrategia se debe usar y cuándo debe hacerlo.

Ofrece la posibilidad de abstraerse de la lógica de las estrategias: si una estrategia es efectiva, entonces la forma en la que funciona no es importante. El enfoque adaptable usa el único criterio seguro para el éxito de una estrategia: su efectividad.

4.3. ¿Merece la pena invertir las señales de la peor estrategia?

El truco de la inversión mostrado arriba puede llevar a pensar en la potencial posibilidad de usarlo en las señales de la peor estrategia. Si una estrategia no resulta rentable (y es la peor de todas), entonces, ¿podemos conseguir beneficios actuando a la inversa?

¿Podemos convertir una estrategia de pérdidas en una rentable con un simple cambio en sus señales? Para responder a esta pregunta, debemos cambiar ArrayMaximum por ArrayMinimum en la función Expert_OnTick() de la clase CAdaptiveStrategy, así como implementar el cambio de direcciones multiplicando el valor de la variable BestDirection por -1.

Además, deberemos comentar la limitación del trading virtual en caso de efectividad negativa (puesto que vamos a analizar el resultado de la peor estrategia):

//if((Performance[BestStrategyIndex]<0) && (RealPositionDirection()==0)) {return;}

El diagrama de beneficios del Asesor Experto adaptable que usa las señales inversas de la peor estrategia se muestra en la figura 7:

Figura 7. Diagramas de beneficios en las cuentas de diez estrategias y el sistema adaptable que usa las señales inversas en el peor sistema

Figura 7. Diagramas de beneficios en las cuentas de diez estrategias y el sistema adaptable que usa las señales inversas en el peor sistema

En este caso, la estrategia con menos éxito durante la mayor parte del tiempo fue la basada en la intersección de medias móviles con el período 3 (MA_3). Como puede ver en la figura 7, la correlación inversa entre MA_3 (de color azul) y la estrategia adaptable (de color rojo) existe, pero el resultado financiero del sistema adaptable no es especialmente impresionante.

Copiar (e invertir) las señales de la peor estrategia no lleva a mejorar la efectividad de las operaciones de trading.

4.3. ¿Por qué el conjunto de medias móviles no es tan eficaz como parece?

En lugar de 10 medias móviles, puede usar muchas más añadiendo otras cien estrategias CStrategyMA con diferentes períodos al contenedor m_all_strategies.

Para ello, cambie ligeramente el código en la clase CAdaptiveStrategy:

for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialization of strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA); 
     }

 

No obstante, debe entender que las medias móviles cercanas se cruzarán inevitablemente; la media guía cambiará constantemente; y el sistema adaptable cambiará sus estados y abrirá/cerrará posiciones con más frecuencia de lo necesario. En consecuencia, las características del sistema adaptable empeorarán. Puede asegurarlo usted mismo comparando las características estadísticas del sistema (la pestaña "Results" (Resultados) del Probador de Estrategias).

Es mejor no hacer sistemas adaptables basados en muchas estrategias con parámetros similares.


5. Consideraciones

El contenedor m_all_strategies puede incluir miles de instancias de estrategias sugeridas, usted incluso puede añadir todas las estrategias con diferentes parámetros. Sin embargo, para ganar el Automated Trading Championship 2010, deberá desarrollar un sistema de gestión de dinero avanzado. Note que hemos usado el volumen de trading igual a 0,1 lotes para hacer una prueba en los datos del historial (y en el código de clases).

5.1 ¿Cómo aumentar la rentabilidad del Asesor Experto Adaptable?

La clase CSampleStrategy tiene la función virtual MoneyManagement_CalculateLots:

//+------------------------------------------------------------------+
//| The function returns the recommended volume for a strategy       |
//| Current volume is passed to it as a parameter                    |
//| Volume can be set depending on:                                  |
//| current m_virtual_balance and m_virtual_equity                   |
//| current statistics of deals (located in m_deals_history)         |
//| or any other thing you want                                      |
//| If a strategy doesn't require change of volume                   |
//| you can return the passed value of volume:  return(trade_volume);|
//+------------------------------------------------------------------+ 
double CSampleStrategy::MoneyManagement_CalculateLots(double trade_volume)
  {
   //return what we've got 
   return(trade_volume); 
  }

 

Para gestionar el volumen de trading puede usar la información estadística sobre los resultados y características de transacciones virtuales grabada en el array m_deals_history[].

Si necesita aumentar el volumen (por ejemplo, duplicarlo si las últimas transacciones virtuales en m_deals_history[] son rentables; o disminuirlo), debe cambiar el valor devuelto de la forma correspondiente.

5.2 Usar las estadísticas de transacciones para el cálculo de rendimiento de estrategia

La función StrategyPerformance(), implementada en la clase CSampleStrategy, sirve para calcular el rendimiento de la estrategia.

//+-----------------------------------------------------------------------+
//| Function StrategyPerformance - the function of strategy effectiveness |
//+-----------------------------------------------------------------------+ 
double CSampleStrategy::StrategyPerformance()
  {
   //returns effectiveness of a strategy
   //in this case it's the difference between the amount
   //of equity at the moment and the initial balance, 
   //i.e. the amount of assets earned by the strategy
   double performance=(m_virtual_equity-m_initial_balance);
   return(performance);
  }

La fórmula de la efectividad de una estrategia puede ser más compleja y, por ejemplo, incluir la efectividad de entrada, salida, la efectividad de transacciones, beneficios, reducciones, etc.

El cálculo de la efectividad de entrada, salida y la efectividad de transacciones (los campos de estructuras entry_eff, exit_eff y trade_eff del array m_deals_history[]) se realiza automáticamente durante el trading virtual (vea la clase CSampeStrategy). Esta información estadística se puede usar para crear usted mismo sus propios cálculos más complejos de la efectividad de la estrategia.

Por ejemplo, como características de efectividad, puede usar el beneficio de las tres últimas transacciones (use el campo pos_Profit field del archivo de transacciones m_deals_history[]):

double CSampleStrategy::StrategyPerformance()
  {
  //if there are deals, multiply this value by the result of three last deals
   if(m_virtual_deals_total>0)
     {
      int avdeals=MathRound(MathMin(3,m_virtual_deals_total));
      double sumprofit=0;
      for(int j=0; j<avdeals; j++)
        {
         sumprofit+=m_deals_history[m_virtual_deals_total-1-j].profit;
        }
      double performance=sumprofit/avdeals;
     }
     return(performance);

   }

Si desea cambiar esta función, cámbiela solo en la clase CSampleStrategy: debe ser igual para todas las estrategias de trading del sistema adaptable. No obstante, debe recordar que la diferencia entre Beneficio y Saldo también es un buen factor de efectividad.

5.3 Usar Take Profit y Stop Loss

Puede cambiar la efectividad de los sistemas de trading estableciendo niveles de stop fijos (se puede hacer llamando a la función Set_Stops; permite establecer los niveles de stop en puntos para el trading virtual). Si se especifican los niveles, los cierres de posiciones virtuales se realizarán automáticamente. Esta funcionalidad se implementa en la clase CSampleStrategy.

En nuestro ejemplo (vea el punto 2.2, la función de clases de medias móviles) se comenta la función de configurar los niveles de stop.

5.4. Restablecimiento periódico del beneficio virtual acumulativo a cero

El enfoque adaptable tiene la misma desventaja que las estrategias normales. Si la estrategia guía entra en pérdida, el sistema adaptable entra en pérdida también. Por esta razón, a veces deberá restablecer a cero los resultados de todas las estrategias y cerrar todas sus posiciones virtuales. 

Para hacerlo, se implementan las siguientes funciones en la clase CSampleStrategy:

// sets a value for cumulative "virtual profit"
 void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_perofit; };
//closes an open virtual position
 void              CloseVirtualPosition();

Una revisión de este tipo se puede usar de vez en cuando; por ejemplo, después de cada barra N.

5.5. Sin milagros

Debe recordar que el sistema adaptable no es una varita mágica (USDJPY H1, 4/01/2010-20/08/2010):

Figura 8. Gráficos de saldo y beneficio del sistema adaptable que usa las señales de las 10 mejores estrategias (USDJPY H1)

Figura 8. Gráficos de saldo y beneficio del sistema adaptable que usa las señales de las 10 mejores estrategias (USDJPY H1)

Curvas de equidad de todas las estrategias que se muestran en la figura 9.

Figura 9. Gráficos de beneficios en la cuenta con el sistema adaptado basado en 10 estrategias (USDJPY H1)

Figura 9. Gráficos de beneficios en la cuenta con el sistema adaptado basado en 10 estrategias (USDJPY H1)

Si no hay estrategias rentables en el sistema adaptable, usarlas no resulta efectivo. Use estrategias rentables.

Debemos tener en cuenta otro detalle interesante. Preste atención al comportamiento de la estrategia adaptable al comienzo de las operaciones de trading:

Figura 10. Gráficos de beneficios en la cuenta con 10 estrategias de la estrategia adaptable

Figura 10. Gráficos de beneficios en la cuenta con 10 estrategias de la estrategia adaptable

Al principio, todas las estrategias tenían resultados negativos, y la estrategia adaptable dejó de realizar operaciones de trading; después, comenzó a alternar estrategias que tenían resultados positivos; y finalmente, todas las estrategias volvieron a resultar no rentables.

Todas las estrategias tienen el mismo saldo al principio. Y solo después de un tiempo, una estrategia u otra pasa a ser la guía; por ello es recomendable configurar un límite en la estrategia adaptable para evitar las operaciones de trading en las primeras barras. Para hacerlo, complemente la función Expert_OnTick de la clase CAdaptiveStrategy con una variable cuyo valor aumente cada vez que aparece una nueva barra.

Al principio, hasta que el mercado elija la mejor estrategia, debe abstenerse de realizar operaciones de trading reales.


Conclusiones

En este artículo hemos tratado un ejemplo del sistema adaptable, que consta de varias estrategias, cada una de la cuales hace sus propias operaciones de trading "virtuales". El trading real se realiza de acuerdo con las señales de la estrategia más rentable en cada momento.

Gracias al enfoque orientado al objeto, las clases para trabajar con datos y clases comerciales de la Biblioteca estándar, la arquitectura del sistema parece sencilla y manejable. Ahora puede crear y analizar sistemas adaptables fácilmente, que incluyen cientos de estrategias de trading.

P.S. Para los problemas con el análisis de comportamiento de sistemas adaptables, adjuntamos la versión depurada de la clase CSampleStrategy (el archivo adaptive-systems-mql5-sources-debug-en.zip). La diferencia de esta versión es la creación de archivos de texto durante su funcionamiento; contienen los informes resumidos de la dinámica de cambios de beneficios/saldo virtuales de las estrategias incluidas en el sistema.