English Русский 中文 Deutsch 日本語
preview
Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 8): Análisis de múltiples estrategias (3) — Política de votación ponderada

Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 8): Análisis de múltiples estrategias (3) — Política de votación ponderada

MetaTrader 5Ejemplos |
19 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Ahora vamos a añadir el último componente a nuestro asesor experto de estrategias múltiples: la estrategia de reversión del Williams Percent Range (WPR). Al igual que hemos hecho en los artículos anteriores, lo primero que hemos hecho ha sido implementar manualmente una versión codificada de la estrategia para comparar su rendimiento con el de la clase que estamos a punto de crear para nuestra aplicación de trading. Sin embargo, para no aburrir a los lectores que han seguido la serie, omitiremos los resultados de las pruebas que utilizamos para validar la clase. Por ahora, basta con decir que los lectores pueden confiar en que se han realizado las pruebas necesarias para verificar la integridad de la clase que presentamos hoy.

Al elaborar un conjunto de estrategias, es lógico que surja la siguiente pregunta: ¿cómo podemos demostrar que todas las estrategias que hemos seleccionado son necesarias? ¿Cómo podemos estar razonablemente seguros de que no obtendríamos mejores resultados con solo unas pocas? ¿Cómo podemos convencernos a nosotros mismos de todo esto?

Afortunadamente para nosotros, el optimizador genético puede ayudarnos a responder a preguntas tan complejas, siempre y cuando le planteemos la pregunta con cuidado. 

Para lograrlo, permitiremos que nuestras estrategias colaboren mediante un sistema democrático, en el que cada estrategia tenga derecho a un solo voto. El peso del voto que aporta cada estrategia puede ser un parámetro de ajuste, modificado por el optimizador genético. Si el optimizador determina que una de nuestras estrategias no está contribuyendo positivamente al rendimiento general, fijará la ponderación del voto de esa estrategia en un valor cercano a cero. Del mismo modo, dará más peso a las estrategias que sean rentables.

Por lo tanto, presentamos este marco como una política de votación ponderada, en la que inicialmente establecemos un nivel de rendimiento de referencia asignando a cada una de nuestras estrategias ponderaciones de voto distribuidas uniformemente. En nuestro ejemplo, partimos de que cada estrategia tiene una ponderación de voto de 0,5, en una escala que va de 0 a 1. 

A partir de ahí, dejamos que el optimizador genético ajuste estos pesos para maximizar la rentabilidad y determinar si las tres estrategias resultan realmente útiles.

Resulta que este procedimiento ofrece una amplia variedad de configuraciones diferentes, cada una de las cuales muestra cómo puede variar la utilidad de una estrategia en función de sus ajustes concretos. En cada configuración concreta, el peso de cada estrategia varía. Por lo tanto, puede darse una situación en la que solo una estrategia resulte útil, mientras que, en otra configuración, las tres aporten positivamente al rendimiento. 

Esto hace que la pregunta «¿Son necesarias las tres estrategias?» resulte realmente difícil de responder. Nuestros resultados sugieren que la respuesta depende de la configuración que la aplicación haya utilizado inicialmente. Empecemos.


Primeros pasos en MQL5

Al finalizar nuestro análisis, nuestro árbol de herencia de estrategias de negociación puede representarse tal y como se muestra en la figura 1 a continuación. Contaremos con tres estrategias de negociación distintas:

  1. Estrategia de momentum del RSI
  2. Estrategia de cruce de medias móviles
  3. Estrategia de inversión de tendencia de rango porcentual de Williams

Todas ellas comparten funcionalidades comunes, como la capacidad de indicar una entrada en largo o en corto. Nuestras tres estrategias comparten una clase base común. Esto es importante para garantizar que mantengamos una funcionalidad uniforme en todas nuestras clases.

En el análisis de hoy nos centraremos en la aplicación de la última de las tres estrategias que se muestran en la figura 1: la estrategia del rango porcentual de Williams. A partir de ahí, nuestro optimizador genético ajustará las ponderaciones asignadas a cada estrategia para garantizar que la menos rentable no merme el rendimiento de nuestra aplicación.

Figura 1: El estado actual de nuestro árbol de herencia compartido por nuestras estrategias de negociación.

Además, en la figura 2 hemos incluido elementos visuales que ayudan a ilustrar el enfoque central de nuestra estrategia. Ten en cuenta que, en la ilustración que se muestra en la figura 2, la suma de las ponderaciones totales de todos los votos no da como resultado uno. Aunque es habitual imponer esa restricción, hemos decidido no hacerlo en este caso. Quizá analicemos esa variante en el futuro, ya que requeriría un algoritmo ligeramente diferente al que se ha implementado hasta ahora.

Por ahora, nos limitamos a permitir que el optimizador genético asigne a cada estrategia un valor entre 0 y 1, inclusive, a cada una de las tres estrategias. A nuestro optimizador genético le resultará más fácil generar estrategias rentables si no tiene que tener en cuenta la estrategia menos rentable. Este es el razonamiento en el que se basa nuestro análisis: queremos demostrar que el optimizador genético también puede podar nuestro árbol de estrategias de negociación al tiempo que ajusta otros parámetros importantes de nuestra estrategia. 

Figura 2: Visualización de las ponderaciones asignadas a cada estrategia por el optimizador genético.

El primer paso para poner en práctica nuestra estrategia consiste en cargar las dependencias. La primera dependencia, tal y como hicimos anteriormente con nuestra estrategia del rango porcentual de Williams (WPR), consiste en cargar la clase del indicador porcentual de Williams de un solo búfer que creamos anteriormente. A continuación, cargamos la clase padre de la estrategia, que también hemos desarrollado en otro apartado.

//+------------------------------------------------------------------+
//|                                                  WPRReversal.mqh |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Dependencies                                                     |
//+------------------------------------------------------------------+
#include <VolatilityDoctor\Indicators\WPR.mqh>
#include <VolatilityDoctor\Strategies\Parent\Strategy.mqh>

Una vez que se hayan cargado nuestras dependencias, podemos empezar a definir los miembros de nuestra clase. El primer elemento hará referencia al indicador «Williams Percent Range» que vamos a utilizar. Este será un miembro privado, el único miembro privado de nuestra clase. Los demás miembros son miembros de la clase pública. En concreto, incluimos el constructor, el destructor y los métodos virtuales heredados de la clase padre.

class WPRReversal : public Strategy
  {
private:
                     //--- The instance of the RSI used in this strategy
                     WPR *my_wpr;

public:
                     //--- Class constructor 
                     WPRReversal(string user_symbol,ENUM_TIMEFRAMES user_timeframe,int user_period);
                     
                     //--- Class destructor
                    ~WPRReversal();
                    
                    //--- Class overrides
                    virtual bool Update(void);
                    virtual bool BuySignal(void);
                    virtual bool SellSignal(void);
  };

Empezamos por sobrescribir el método «update». El método «update» simplemente llama al método «set_indicator_values», que es una función presente en todas nuestras clases de indicadores. Esta función introduce las lecturas del WPR procedentes del terminal en nuestro búfer de indicadores. Realiza una comprobación preventiva para asegurarse de que el número de lecturas no sea cero antes de devolver el control al contexto de llamada.

//+------------------------------------------------------------------+
//| Our strategy update method                                       |
//+------------------------------------------------------------------+
bool WPRReversal::Update(void)
   {
      //--- Set the indicator value
      my_wpr.SetIndicatorValues(Strategy::GetIndicatorBufferSize(),true);
      
      //--- Check readings are valid
      if(my_wpr.GetCurrentReading() != 0) return(true);
      
      //--- Something went wrong
      return(false);
   }  

A partir de ahí, definimos dos métodos que utilizamos para señalar nuestras entradas de compra y venta. Estos métodos simplemente devuelven el valor «true» si se cumplen sus respectivas condiciones.

//+------------------------------------------------------------------+
//| Check for our buy signal                                         |
//+------------------------------------------------------------------+
bool WPRReversal::BuySignal(void)
   {
      //--- Buy signals when the RSI is above 50
      return(my_wpr.GetCurrentReading()>50);
   }

//+------------------------------------------------------------------+
//| Check for our sell signal                                        |
//+------------------------------------------------------------------+
bool WPRReversal::SellSignal(void)
   {
      //--- Sell signals when the RSI is below 50
      return(my_wpr.GetCurrentReading()<50);
   }

Por último, definimos nuestro constructor paramétrico, que toma como argumentos el símbolo, el marco temporal y el período con los que debe inicializarse el indicador WPR. A continuación, el destructor simplemente elimina el puntero que hemos creado hacia la nueva instancia de nuestro objeto de la clase WPR.

//+------------------------------------------------------------------+
//| Our class constructor                                            |
//+------------------------------------------------------------------+
WPRReversal::WPRReversal(string user_symbol,ENUM_TIMEFRAMES user_timeframe,int user_period)
  {
   my_wpr = new WPR(user_symbol,user_timeframe,user_period);
   Print("WPRReversal Strategy Loaded.");
  }
  
//+------------------------------------------------------------------+
//| Our class destructor                                             |
//+------------------------------------------------------------------+
WPRReversal::~WPRReversal()
  {
   delete my_wpr;
  }
//+------------------------------------------------------------------+


Creación del asesor experto

Ahora vamos a empezar a definir el asesor experto que utilizaremos en nuestra configuración actual. La primera parte de nuestro asesor experto estará dedicada a las constantes del sistema, que mantendremos fijas para garantizar la reproducibilidad de nuestras pruebas. Hay que fijar algunos parámetros sencillos, como el desplazamiento de la media móvil y el tipo de media móvil que queremos utilizar. Se establecerán con valores fáciles de recordar, como, por ejemplo, un desplazamiento de cero.

//+------------------------------------------------------------------+
//|                                                   MSA Test 1.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
//--- Fix any parameters that can afford to remain fixed
#define MA_SHIFT         0
#define MA_TYPE          MODE_EMA
#define RSI_PRICE        PRICE_CLOSE

Además, debemos aceptar ciertas entradas de los usuarios. Recuerda nuestra analogía: nuestra intención es aceptar estas sugerencias de entrada del optimizador genético. Los tres primeros grupos de datos de entrada deberían resultarle bastante familiares al lector. Estos son, sencillamente, los períodos que vamos a utilizar con nuestros indicadores técnicos.

El grupo de entradas que nos interesa especialmente en este análisis es el último: el grupo de parámetros de estrategia global. Ahí es donde se guardarán los pesos que estamos definiendo hoy. Parámetros como el periodo de tenencia y el horizonte temporal de la estrategia ya deberían resultar familiares a nuestros lectores habituales. No obstante, los nuevos lectores deben saber que el «período de tenencia» se refiere al tiempo que esperaremos antes de considerar que una posición ha llegado a su vencimiento y debe cerrarse. Como es lógico, este periodo de tenencia depende del horizonte temporal de la estrategia. Por ejemplo, un periodo de mantenimiento de 5 en un marco temporal de M10 significa que mantendremos la posición durante 50 minutos antes de cerrarla.

//+------------------------------------------------------------------+
//| User Inputs                                                      |
//+------------------------------------------------------------------+
input   group          "Moving Average Strategy Parameters"
input   int             MA_PERIOD                       =        10;//Moving Average Period

input   group          "RSI Strategy Parameters"
input   int             RSI_PERIOD                      =         15;//RSI Period

input   group          "WPR Strategy Parameters"
input   int             WPR_PERIOD                      =         30;//WPR Period

input   group          "Global Strategy Parameters"
input   ENUM_TIMEFRAMES STRATEGY_TIME_FRAME             = PERIOD_D1;//Strategy Timeframe
input   int             HOLDING_PERIOD                  =         5;//Position Maturity Period
input   double          weight_1                        =       0.5;//Strategy 1 vote weight
input   double          weight_2                        =       0.5;//Strategy 2 vote weight
input   double          weight_3                        =       0.5;//Strategy 3 vote weight

A continuación se indican las dependencias que necesitará nuestra aplicación de negociación. La primera dependencia es la biblioteca «trade», que constituye nuestra dependencia base. Nos ayuda a gestionar las posiciones. A partir de ahí, contamos con otras dependencias desarrolladas a medida, como TimeInfo y TradeInfo, que nos ayudan a saber cuándo podemos actuar en función de la información del mercado, además de proporcionarnos acceso a los niveles mínimos de negociación, el precio de compra (ask) y el valor mínimo negociable, respectivamente.

Las tres dependencias restantes proceden de las clases de estrategia que hemos ido creando juntos a lo largo de esta serie. Seguramente ya te resultarán familiares; y si eres un lector nuevo, al menos la última dependencia te resultará reconocible, porque es la que hemos creado juntos hoy.

//+------------------------------------------------------------------+
//| Dependencies                                                     |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <VolatilityDoctor\Time\Time.mqh>
#include <VolatilityDoctor\Trade\TradeInfo.mqh>
#include <VolatilityDoctor\Strategies\OpenCloseMACrossover.mqh>
#include <VolatilityDoctor\Strategies\RSIMidPoint.mqh>
#include <VolatilityDoctor\Strategies\WPRReversal.mqh>

También necesitaremos algunas variables globales, como los controladores para nuestros objetos personalizados y un temporizador que nos ayude a llevar un control de cuánto tiempo queda para el vencimiento de nuestras posiciones.

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+

//--- Custom Types
CTrade               Trade;
Time                 *TradeTime;
TradeInfo            *TradeInformation;
RSIMidPoint          *RSIMid;
OpenCloseMACrossover *MACross;
WPRReversal          *WPRR;

//--- System Types
int                  position_timer;

Cuando se inicie nuestra aplicación por primera vez, crearemos nuevas instancias de nuestras clases definidas de forma personalizada, como las estrategias y la clase TradeInfo. 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create dynamic instances of our custom types
   TradeTime        = new Time(Symbol(),STRATEGY_TIME_FRAME);
   TradeInformation = new TradeInfo(Symbol(),STRATEGY_TIME_FRAME);
   MACross          = new OpenCloseMACrossover(Symbol(),STRATEGY_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE);
   RSIMid           = new RSIMidPoint(Symbol(),STRATEGY_TIME_FRAME,RSI_PERIOD,RSI_PRICE);
   WPRR             = new WPRReversal(Symbol(),STRATEGY_TIME_FRAME,WPR_PERIOD);
//--- Everything was fine
   return(INIT_SUCCEEDED);
  }
//--- End of OnInit Scope

Cuando la aplicación ya no se utilice, eliminaremos estos objetos definidos por el usuario para asegurarnos de que no se produzca ninguna fuga de memoria.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete the dynamic objects
   delete TradeTime;
   delete TradeInformation;
   delete MACross;
   delete RSIMid;
  }
//--- End of Deinit Scope

Cada vez que se reciban nuevos datos de precios, comprobaremos primero si se ha formado una nueva vela. Si ese es el caso, actualizaremos los parámetros y los valores de los indicadores de nuestras estrategias. Por último, si no tenemos posiciones abiertas, reiniciaremos nuestro temporizador de posiciones y comprobaremos si se cumplen las condiciones de la señal. De lo contrario, si ya hay posiciones abiertas, haremos un seguimiento de cuánto falta para el vencimiento mientras nos preparamos para liquidar la posición.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Check if a new daily candle has formed
   if(TradeTime.NewCandle())
     {
      //--- Update strategy
      Update();

      //--- If we have no open positions
      if(PositionsTotal() == 0)
        {
         //--- Reset the position timer
         position_timer = 0;

         //--- Check for a trading signal
         CheckSignal();
        }
      //--- Otherwise
      else
        {
         //--- The position has reached maturity
         if(position_timer == HOLDING_PERIOD)
            Trade.PositionClose(Symbol());
         //--- Otherwise keep holding
         else
            position_timer++;
        }
     }
  }
//--- End of OnTick Scope

El método «update» se implementa simplemente llamando a la función «update» asociada a cada una de nuestras estrategias. 

//+------------------------------------------------------------------+
//| Update our technical indicators                                  |
//+------------------------------------------------------------------+
void Update(void)
  {
//--- Update the strategy
   RSIMid.Update();
   MACross.Update();
   WPRR.Update();
  }
//--- End of Update Scope

El método `check_signal` resulta interesante por cómo está configurado. Es decir, empezamos por inicializar el recuento total de votos en cero. Si al final del proceso el resultado global es positivo, tomamos una posición larga. De lo contrario, si el resultado global de la votación es negativo, vendemos. A partir de ahí, comprobamos qué señal genera cada estrategia. Si una estrategia genera una señal de compra, sumamos la ponderación de dicha estrategia al voto total. Si genera una señal de venta, restamos la ponderación de la estrategia del total de votos. Cada estrategia dispone de un turno para votar. Al final, evaluamos el resultado total de la votación según las reglas que acabamos de describir.

//+------------------------------------------------------------------+
//| Check for a trading signal using our cross-over strategy         |
//+------------------------------------------------------------------+
void CheckSignal(void)
  {
   double vote = 0;

   if(MACross.BuySignal())
      vote += weight_1;
   else
      if(MACross.SellSignal())
         vote -= weight_1;

   if(RSIMid.BuySignal())
      vote += weight_2;
   else
      if(RSIMid.SellSignal())
         vote -= weight_2;

   if(WPRR.BuySignal())
      vote += weight_3;
   else
      if(WPRR.SellSignal())
         vote -= weight_3;

//--- Long positions when the close moving average is above the open
   if(vote > 0)
     {
      Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),0,0,"");
      return;
     }

//--- Otherwise short
   else
      if(vote < 0)
        {
         Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),0,0,"");
         return;
        }
  }
//--- End of CheckSignal Scope

Al igual que con cualquier aplicación, debemos terminar unificando todas las constantes del sistema que hemos creado.

//+------------------------------------------------------------------+
//| Undefine system constants                                        |
//+------------------------------------------------------------------+
#undef MA_SHIFT
#undef RSI_PRICE
#undef MA_TYPE
//+------------------------------------------------------------------+

Ya estamos listos para empezar a probar y optimizar nuestra estrategia de trading. Empezamos seleccionando el asesor experto que acabamos de crear juntos. A continuación, especificamos el símbolo en el que vamos a probar nuestra aplicación. Hemos estado utilizando el par EURUSD a lo largo de nuestro análisis en el marco temporal diario, tal y como se ha indicado anteriormente. Las fechas de las pruebas se seleccionarán utilizando un intervalo personalizado, y hemos estado realizando pruebas desde febrero de 2023 hasta mayo de 2025.

Figura 3: Los parámetros y las fechas que utilizaremos para nuestra optimización genética

Para la prueba forward, seleccionamos la mitad de los datos: la primera mitad se utilizará para el backtest y la segunda para la prueba forward. La prueba retrospectiva tiene por objeto mostrar qué estrategias son estables y cuáles es probable que se hayan adaptado en exceso a los resultados de la prueba retrospectiva. Siempre seleccionamos un retraso aleatorio para lograr la simulación más auténtica posible de los acontecimientos del mercado, y el modelado debe hacerse con ticks reales.

Figura 4: Indicación al optimizador genético de en qué valores y en qué intervalos debe buscar los parámetros de nuestras estrategias.

Por último, para la optimización, selecciona el algoritmo genético rápido. El número total de parámetros de nuestra estrategia es un aspecto concreto que nos hemos esforzado al máximo por controlar y limitar. Pero, como podemos ver, el número total de pasos necesarios para optimizar la estrategia —incluso con parámetros modestos— ha aumentado considerablemente. De hecho, ha crecido muchísimo en solo un paso. Por lo tanto, consideré necesario que trasladáramos parte del trabajo a MQL5 Cloud. Para seguir las instrucciones, primero debes iniciar sesión en tu cuenta de MQL5 y tener un saldo positivo. 

Figura 5: Inicio de sesión en tu cuenta de usuario de MQL5 a través de la terminal MetaTrader 5.

Solo tienes que iniciar el proceso de optimización, hacer clic con el botón derecho del ratón en el número de núcleos disponibles en tu equipo y seleccionar «Usar MQL5 Cloud Network». Todo esto se realiza en la pestaña «Agentes» del Probador de estrategias.

Figura 6: Activación de la nube MQL5 para acelerar las pruebas retrospectivas

Una vez que actives MQL5 Cloud, algunas de las tareas que se están realizando en tu ordenador se trasladarán a la nube. Esto ayudará a acelerar el proceso de optimización y, con un poco de suerte, obtendremos los resultados más rápido, siempre y cuando la red sea segura y fiable.

Figura 7: Conexión a cualquiera de los centros de datos disponibles cerca de ti.

Los resultados de la optimización reflejan las pruebas realizadas con datos históricos a los que tiene acceso el optimizador genético. Esto le permite evaluar el rendimiento de la estrategia y ajustar los parámetros en consecuencia para mejorar dicho rendimiento. Sin embargo, el optimizador genético no tiene acceso a los resultados de la prueba forward, ya que estos reflejan el rendimiento fuera de muestra.

A partir de los resultados de las pruebas retrospectivas, podemos observar que los niveles de beneficios coinciden con los alcanzados en versiones anteriores de nuestra aplicación de negociación. Al analizar las estrategias con mejor rendimiento, observamos que las ponderaciones asignadas a cada subestrategia se sitúan en un intervalo comprendido entre 0,4 y 0,8. Estas ponderaciones, que han obtenido los mejores resultados, están todas bastante próximas entre sí, lo que sugiere que la configuración más eficaz en la prueba retrospectiva fue la que empleó todas las estrategias.

Figura 8: Resultados del backtest de nuestro proceso de optimización genética.

Sin embargo, al analizar la prueba forward, observamos que las estrategias con mejor rendimiento se basaban, en su mayoría, en solo dos estrategias. De hecho, las estrategias mejor clasificadas otorgaban una ponderación mínima a la Estrategia Tres; algunas incluso le asignaban una ponderación de cero.

Lo que resulta desalentador es que solo unas pocas de las estrategias con mejor rendimiento en la prueba forward también resultaron rentables en el backtest. Sin embargo, entre las estrategias que obtuvieron buenos resultados en ambas pruebas, volvimos a observar que la Estrategia Tres tenía ponderaciones reducidas, incluso en las configuraciones más estables que pudimos encontrar.

Esto nos lleva, por tanto, a plantearnos la posibilidad de descartar la Estrategia Tres, ya que no contribuyó de manera significativa a las estrategias con mejor rendimiento en la prueba retrospectiva. Sin embargo, esta conclusión se basa en la configuración más rentable que se ha encontrado, y no siempre es aconsejable sacar conclusiones de esta manera, ya que puede llevarnos a un «sobreajuste» de nuestras decisiones a los datos disponibles.

Sin embargo, al analizar todas las estrategias que resultaron rentables en ambas pruebas, observamos que, en general, en la mayoría de los casos las tres estrategias tenían ponderaciones relativamente similares entre sí. Solo en este caso concreto, que destaca sobre los demás, la Estrategia Tres tuvo la menor ponderación y el mejor rendimiento. Es complicado tomar decisiones en un contexto de tanta incertidumbre. Sin embargo, esa es la naturaleza del reto al que nos enfrentamos.

Por lo tanto, al considerar que nuestras acciones están en consonancia con el mejor rendimiento posible, llegaremos a la conclusión de que la Estrategia Tres quizá no sea tan importante, y seguiremos utilizando únicamente las dos primeras estrategias.

Figura 9: Los resultados forward de nuestro proceso de optimización genética sugieren que la estrategia 3 quizá no sea tan importante para nuestro éxito.


Conclusión

Como se ha podido observar en nuestro análisis, determinar el número óptimo de estrategias que deben utilizarse en una aplicación que combina múltiples estrategias puede suponer un reto considerable. No siempre sabemos desde el principio si necesitaremos una, cinco o diez estrategias diferentes.

Sin embargo, la conclusión principal es que nuestro optimizador genético puede ayudarnos a abordar este tipo de cuestiones complejas con facilidad. También cabe destacar que el optimizador genético puede considerarse una herramienta mucho más potente que el popular ChatGPT y otros modelos de lenguaje grande (LLM) en los que los desarrolladores pueden confiar para responder a algunas de las preguntas que se plantean en su labor de creación de aplicaciones algorítmicas.

Como ya hemos comentado anteriormente en nuestra serie de artículos relacionada, «Superar las limitaciones de la IA». Nos hemos dado cuenta de que los algoritmos específicos para un ámbito concreto son, intrínsecamente, más valiosos para nosotros que los algoritmos de uso general. ChatGPT y otros modelos de lenguaje grande (LLM) similares son algoritmos de uso general, mientras que el optimizador genético integrado en tu copia de MetaTrader 5 es un algoritmo específico para este ámbito, lo que lo hace mucho más eficaz que intentar plantear la misma pregunta a ChatGPT.

También hemos mostrado cómo puedes empezar a utilizar MQL5 Cloud para acelerar tus procesos de backtesting y optimización. Es posible que este artículo no se hubiera terminado a tiempo sin el uso de MQL5 Cloud. Es fácil empezar y las tarifas son muy asequibles.

De hecho, dispones de un servicio de computación en la nube las 24 horas del día, con múltiples centros de datos redundantes. Por si acaso perdieras la conexión debido a problemas de red, seguirás conectado casi siempre. En definitiva, MQL5 Cloud y el Optimizador Genético son herramientas indispensables en el conjunto de herramientas del desarrollador de algoritmos moderno.

Este ejercicio nos servirá de guía en nuestros procesos de toma de decisiones a medida que avancemos en el desarrollo de modelos estadísticos para nuestra estrategia de negociación.

En las próximas entregas veremos que las dos primeras estrategias pueden ser suficientes para desarrollar una tarea de clasificación binaria en la que resulte más rentable la Estrategia Uno o la Estrategia Dos. A continuación, compararemos el rendimiento de nuestra aplicación de modelización estadística con la estrategia inicial que desarrollamos juntos.

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

Archivos adjuntos |
MSA_Test_3.mq5 (6.82 KB)
WPRReversal.mqh (3.44 KB)
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 31): Motor de reconocimiento de velas japonesas en Python (I) — Detección manual Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 31): Motor de reconocimiento de velas japonesas en Python (I) — Detección manual
Los patrones de velas japonesas son fundamentales para el trading basado en la evolución de los precios, ya que ofrecen información valiosa sobre posibles reversiones o continuaciones del mercado. Imagina una herramienta fiable que supervise continuamente cada nueva barra de precios, identifique formaciones clave como los patrones envolvente, martillo, doji y estrella, y te avise de inmediato cuando detecte una oportunidad de negociación significativa. Esta es precisamente la funcionalidad que hemos desarrollado. Tanto si eres nuevo en el mundo del trading como si eres un profesional con experiencia, este sistema ofrece alertas en tiempo real sobre patrones de velas japonesas, lo que te permite centrarte en ejecutar operaciones con mayor confianza y eficiencia. Sigue leyendo para descubrir cómo funciona y cómo puede mejorar tu estrategia de trading.
Robot de trading basado en redes neuronales sobre la moderna arquitectura Mamba con modelo selectivo de espacio de estados (SSM) Robot de trading basado en redes neuronales sobre la moderna arquitectura Mamba con modelo selectivo de espacio de estados (SSM)
El artículo analiza la revolucionaria arquitectura de red neuronal Mamba/SSM para la predicción de series temporales financieras. Se presenta una implementación completa en MQL5 de una alternativa moderna a la arquitectura del Transformer con una complejidad lineal O(N) en lugar de cuadrática O(N²). Se analizan en detalle los modelos selectivos de espacio de estados (Selective State Space Models, SSM), las optimizaciones conscientes del hardware, las técnicas de segmentación en parches (patching) y los métodos avanzados de entrenamiento con AdamW. Se incluyen los resultados prácticos de las pruebas, que mostraron un aumento de la precisión del 62 % al 71 %, junto con una reducción del tiempo de entrenamiento de 45 a 8 minutos. Se presenta un asesor experto listo para usar, con autoaprendizaje y gestión adaptativa del riesgo, para MetaTrader 5.
Implementación de un circuito cuántico para Quantum Reservoir Computing (QRC) Implementación de un circuito cuántico para Quantum Reservoir Computing (QRC)
Hoy hablaremos de un enfoque revolucionario del aprendizaje automático en el trading mediante la computación cuántica. El artículo muestra la implementación práctica de un sistema QRC adaptativo con aprendizaje adicional continuo para predecir los movimientos del mercado en tiempo real.
Redes neuronales en el trading: descomposición en lugar de escalado (SSCNN) Redes neuronales en el trading: descomposición en lugar de escalado (SSCNN)
En este artículo iniciamos la presentación del framework SSCNN, una solución arquitectónica moderna para el análisis de series temporales que combina precisión, una estructura definida y una elevada eficiencia computacional. Analizaremos paso a paso sus aspectos teóricos, prestaremos atención a las diferencias clave con respecto a sus predecesores y comenzaremos la implementación práctica de los componentes básicos en el entorno MQL5.