English Русский Deutsch 日本語
preview
Cómo implementar la optimización automática en los asesores expertos de MQL5

Cómo implementar la optimización automática en los asesores expertos de MQL5

MetaTrader 5Ejemplos | 30 abril 2025, 09:15
24 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Prepárese para ser introducido al maravilloso mundo de los algoritmos de trading de Forex con optimización automática. Puede permitir que su Asesor Experto (EA) se ajuste a la siguiente iteración de negociación en función de cómo sean las condiciones del mercado después de realizar una operación.

Considere su EA como un operador experto que observa las tendencias a través de las medias móviles. Funcionaba, pero ¿y si fuera una caja negra consciente del mercado que aún pudiera aprender a afinar su estrategia con el tiempo? Este es el proceso que se autooptimiza.

Una de las principales ventajas de utilizar un Asesor Experto (EA) es que puede adaptarse en última instancia a las condiciones del mercado a medida que estas cambian. El EA se adapta automáticamente al entorno actual del mercado, reduciendo la necesidad de supervisión manual constante y el cambio de parámetros. Esto permite a los traders aprovechar constantemente las oportunidades por segundo a corto plazo y ejecutar su estrategia comercial sin interrupciones. Además, el EA es capaz de ajustar las estrategias comerciales todos los días y durante todo el tiempo.

Sin embargo, hay algunos peligros que debemos tener en cuenta. Un desafío es el riesgo de sobreajuste a los datos recientes, lo que puede conducir a un desempeño deficiente en diferentes condiciones de mercado. Gestionar eficazmente los recursos computacionales también es crucial, ya que puede surgir una mayor complejidad del código al automatizar estrategias. La estabilidad durante los cambios de parámetros puede ser difícil de mantener y, atribuir el rendimiento, a veces puede convertirse en un problema.

En esta guía, exploraremos el proceso de creación de un EA con optimización automática, incluida la automatización de estrategias mediante indicadores personalizados. Cubriremos la lógica de optimización robusta, las mejores prácticas para la selección de parámetros y cómo reconstruir estrategias con pruebas retrospectivas. Además, se discutirán métodos de nivel superior, como la optimización del avance, para mejorar su enfoque comercial.

Cómo alcanzar tus objetivos, ser productivo (¡y cómo no caer en trampas!)

Basaremos nuestro plan de trading en una estrategia de cruce de medias móviles, que es la más básica de todas las estrategias pero nunca falla en mercados con tendencia.


1. Configuración de bibliotecas, parámetros de entrada y rangos de optimización para la optimización automática de un EA

1.1 Importación de bibliotecas necesarias

En la fila inicial mediante la inclusión de las bibliotecas MQL5 necesarias:

#include <Trade\Trade.mqh>
#include <Arrays\ArrayObj.mqh>

La biblioteca Trade proporciona funciones para ejecutar operaciones, mientras que la biblioteca ArrayObj nos permite trabajar con matrices dinámicas de objetos, que utilizaremos para almacenar los resultados de la optimización.

1.2 Definición de los parámetros de entrada

A continuación, definimos los parámetros de entrada para nuestro EA:

input int MA_Fast_Period = 10;     // Fast Moving Average Period
input int MA_Slow_Period = 20;     // Slow Moving Average Period
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method
input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price
input double LotSize = 0.1;        // Lot Size
input int StopLoss = 50;           // Stop Loss in points
input int TakeProfit = 100;        // Take Profit in points

// Optimization parameters
input bool AutoOptimize = false;   // Enable Auto Optimization
input int OptimizationPeriod = 5000; // Number of ticks between optimizations
input int MinDataPoints = 1000;    // Minimum number of data points for optimization

Estos parámetros de entrada permiten al usuario configurar el comportamiento del EA y la configuración de optimización directamente desde la interfaz de MetaTrader.

1.3 Variables y manejadores globales

A continuación, declaramos las variables globales y los manejadores que se utilizarán en todo el EA:

CTrade trade;
int fastMA_Handle, slowMA_Handle;
double fastMA[], slowMA[];
int tickCount = 0;
CArrayObj* optimizationResults;

// Optimization ranges
const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1;
const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1;

El objeto `CTrade` gestiona las operaciones comerciales, mientras que `fastMA_Handle` y `slowMA_Handle` se utilizan para gestionar los indicadores de medias móviles. La matriz `optimizationResults` almacenará los resultados de nuestras pruebas de optimización.

1.4 Configuración de la optimización

Los ajustes de optimización definen el rango de valores que probaremos para cada parámetro:

  • Periodo MA rápido: De 5 a 50, paso a paso de 1 en 1 Periodo MA lento: De 10 a 100, en pasos de 1
Estos rangos pueden ajustarse en función de sus necesidades específicas y de las características del instrumento con el que esté operando.


2. Aplicación de la lógica comercial básica

Con nuestra estructura de EA configurada, implementemos las funciones centrales que manejarán la inicialización, desinicialización y procesamiento de ticks.

2.1 La función OnInit()

La función `OnInit()` se ejecuta cuando el EA se carga por primera vez en un gráfico. Así es como lo ponemos en práctica:

int OnInit()
{
    // Initialize MA handles
    fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
    slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
    
    if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators");
        return INIT_FAILED;
    }
    
    // Initialize optimization results array
    optimizationResults = new CArrayObj();
    
    return INIT_SUCCEEDED;
}

Esta función crea los indicadores de media móvil e inicializa la matriz de resultados de optimización. Si los indicadores no se inicializan, el EA no se iniciará.

2.2 La función OnDeinit()

La función `OnDeinit()` es llamada cuando el EA es eliminado del gráfico o cuando se cierra el terminal:

void OnDeinit(const int reason)
{
    // Release MA handles
    IndicatorRelease(fastMA_Handle);
    IndicatorRelease(slowMA_Handle);
    
    // Clean up optimization results
    if(optimizationResults != NULL)
    {
        delete optimizationResults;
        optimizationResults = NULL;
    }
}

Esta función asegura que liberamos correctamente los manejadores de los indicadores y liberamos la memoria utilizada por la matriz de resultados de optimización.

2.3 La función OnTick()

La función `OnTick()` es el corazón de nuestro EA, llamado en cada tick del símbolo seleccionado:

void OnTick()
{
    // Check if we have enough bars to calculate MAs
    if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return;
    
    // Copy MA values
    if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return;
    if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return;
    
    // Auto Optimization
    if(AutoOptimize && ++tickCount >= OptimizationPeriod)
    {
        Optimize();
        tickCount = 0;
    }
    
    // Trading logic
    if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0])
    {
        // Open buy position
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point);
    }
    else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0])
    {
        // Open sell position
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point);
    }
}

Esta función realiza varias tareas clave:

  1. Comprueba si hay suficientes datos históricos para calcular las medias móviles.
  2. Recupera los valores actuales de las medias móviles.
  3. Si la optimización automática está activada y es el momento de optimizar (basándose en el recuento de ticks), llama a la función `Optimize()`.
  4. Implementa la lógica de negociación, abriendo posiciones de compra o venta en función de los cruces de medias móviles.



3. Aplicación de la lógica de optimización

El núcleo de nuestra funcionalidad de autooptimización reside en la función `Optimize()`. Desglosémoslo y examinemos sus componentes.

3.1 La función Optimize()

Esta es la estructura general de la función `Optimize()`:

void Optimize()
{
    Print("Starting optimization...");
    
    optimizationResults.Clear();
    
    // Loop through all combinations of MA periods
    for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
    {
        for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
        {
            if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
            
            double profit = TestParameters(fastPeriod, slowPeriod);
            
            OptimizationResult* result = new OptimizationResult;
            result.fastPeriod = fastPeriod;
            result.slowPeriod = slowPeriod;
            result.profit = profit;
            
            optimizationResults.Add(result);
        }
    }
    
    // Find the best result
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
// Update the EA parameters
        MA_Fast_Period = bestResult.fastPeriod;
        MA_Slow_Period = bestResult.slowPeriod;
        
        // Update indicator handles
        IndicatorRelease(fastMA_Handle);
        IndicatorRelease(slowMA_Handle);
        fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
        slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
        
        Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period);
    }
    else
    {
        Print("Optimization failed to find better parameters.");
    }
}

3.2 Bucle a través de combinaciones de parámetros

Los bucles for anidados en la función `Optimize()` nos permiten probar todas las combinaciones de periodos de medias móviles rápidas y lentas dentro de nuestros rangos especificados. Esto se conoce como un enfoque de optimización de «fuerza bruta».

for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
{
    for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
    {
        if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
        
        double profit = TestParameters(fastPeriod, slowPeriod);
        
        // Store results...
    }
}

Omitimos las combinaciones en las que el periodo lento es menor o igual que el rápido, ya que no tendría sentido para nuestra estrategia.

3.3 Almacenamiento y comparación de resultados

Para cada combinación válida, llamamos a `TestParameters()` para evaluar su rendimiento. Los resultados se almacenan en un objeto `OptimizationResult` y se añaden a nuestra matriz `optimizationResults`.

Después de probar todas las combinaciones, hacemos un bucle con los resultados para encontrar el mejor conjunto de parámetros:

OptimizationResult* bestResult = NULL;
for(int i = 0; i < optimizationResults.Total(); i++)
{
    OptimizationResult* currentResult = optimizationResults.At(i);
    if(bestResult == NULL || currentResult.profit > bestResult.profit)
    {
        bestResult = currentResult;
    }
}

Si se encuentra el mejor resultado, actualizamos los parámetros del EA y volvemos a crear las asas del indicador con los nuevos periodos.


4. Parámetros de prueba

La función `TestParameters()` es crucial para evaluar cada conjunto de parámetros. Examinémoslo en detalle.

4.1 La función TestParameters()

double TestParameters(int fastPeriod, int slowPeriod)
{
    int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
    int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);
    
    if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators for testing");
        return -DBL_MAX;
    }
    
    double fastBuffer[], slowBuffer[];
    ArraySetAsSeries(fastBuffer, true);
    ArraySetAsSeries(slowBuffer, true);
    
    int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer);
    copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer));
    
    if(copied < MinDataPoints)
    {
        Print("Not enough data for testing");
        return -DBL_MAX;
    }
    
    double profit = 0;
    for(int i = 1; i < copied; i++)
    {
        if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
        {
            // Buy signal
            profit += Close[i-1] - Open[i];
        }
        else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
        {
            // Sell signal
            profit += Open[i] - Close[i-1];
        }
    }
    
    IndicatorRelease(maFast);
    IndicatorRelease(maSlow);
    
    return profit;
}

4.2 Creación de indicadores temporales

Para cada conjunto de parámetros que probamos, creamos indicadores temporales de medias móviles:

int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);

Estos indicadores temporales nos permiten calcular medias móviles con diferentes periodos sin afectar a nuestra lógica principal de negociación.

4.3 Simulación de operaciones

A continuación, hacemos un bucle con los datos históricos, simulando operaciones basadas en nuestra lógica de cruce de medias móviles:

for(int i = 1; i < copied; i++)
{
    if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
    {
        // Buy signal
        profit += Close[i-1] - Open[i];
    }
    else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
    {
        // Sell signal
        profit += Open[i] - Close[i-1];
    }
}

Esta simulación simplificada supone que podemos abrir una operación al precio de apertura de la barra que sigue a un cruce y cerrarla al precio de cierre de la misma barra.

4.4 Cálculo del beneficio

La función devuelve el beneficio total generado por las operaciones simuladas. En una aplicación más sofisticada, podría considerar otros factores como la reducción máxima, el ratio de Sharpe o la tasa de ganancias.


5. Aplicación de parámetros optimizados

Una vez que hayamos encontrado los parámetros de mejor rendimiento, tenemos que aplicarlos a nuestro EA.

5.1 Actualización de los parámetros de EA

Actualizamos nuestras variables globales con los nuevos valores óptimos:

MA_Fast_Period = bestResult.fastPeriod;
MA_Slow_Period = bestResult.slowPeriod;

5.2 Recreación de los manejadores de los indicadores

Después de actualizar los parámetros, tenemos que volver a crear nuestro indicador maneja:

IndicatorRelease(fastMA_Handle);
IndicatorRelease(slowMA_Handle);
fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);

Esto garantiza que nuestra lógica de negociación principal utilizará los parámetros recién optimizados en el futuro.


6. Técnicas avanzadas de optimización

Aunque nuestra implementación actual proporciona una base sólida para la optimización automática, existen varias técnicas avanzadas que puede considerar para mejorar aún más el rendimiento de su EA.

6.1 Optimización multicriterio

En lugar de optimizar únicamente en función de los beneficios, puede incorporar múltiples criterios a su proceso de optimización. Por ejemplo:

struct OptimizationResult
{
    int fastPeriod;
    int slowPeriod;
    double profit;
    double drawdown;
    double sharpeRatio;
};

double CalculateScore(const OptimizationResult &result)
{
    return result.profit * 0.5 + result.sharpeRatio * 0.3 - result.drawdown * 0.2;
}

Este enfoque permite equilibrar múltiples aspectos del rendimiento, lo que puede dar lugar a conjuntos de parámetros más sólidos.

6.2 Optimización progresiva

La optimización progresiva consiste en dividir los datos históricos en varios segmentos, optimizar un segmento y probar el siguiente.

Esto puede ayudar a evitar el sobreajuste:

void WalkForwardOptimization()
{
    int totalBars = Bars(_Symbol, PERIOD_CURRENT);
    int segmentSize = 1000; // Adjust as needed
    
    for(int i = 0; i < totalBars - 2*segmentSize; i += segmentSize)
    {
        // Optimize on segment i to i+segmentSize
        OptimizeSegment(i, i+segmentSize);
        
        // Test on segment i+segmentSize to i+2*segmentSize
        TestSegment(i+segmentSize, i+2*segmentSize);
    }
}

6.3 Ajuste adaptativo de parámetros

En lugar de sustituir completamente los parámetros durante la optimización, puede implantar un sistema que ajuste gradualmente los parámetros en función del rendimiento reciente:

void AdaptParameters()
{
    double recentPerformance = CalculateRecentPerformance();
    double adaptationRate = 0.1; // Adjust as needed
    
    MA_Fast_Period += (int)((bestResult.fastPeriod - MA_Fast_Period) * adaptationRate * recentPerformance);
    MA_Slow_Period += (int)((bestResult.slowPeriod - MA_Slow_Period) * adaptationRate * recentPerformance);
}

Este enfoque puede proporcionar una transición más suave entre los conjuntos de parámetros y reducir potencialmente el impacto del ruido del mercado a corto plazo en su proceso de optimización.



7. Buenas prácticas y consideraciones

A medida que implemente y perfeccione su EA de optimización automática, tenga en cuenta estas prácticas recomendadas:

7.1 Elección de la frecuencia de optimización

La frecuencia de la optimización puede influir significativamente en el rendimiento de su EA. Optimizar con demasiada frecuencia puede llevar a reaccionar de forma exagerada a las fluctuaciones del mercado a corto plazo, mientras que optimizar con muy poca frecuencia puede hacer que se pierdan oportunidades.

Considere la posibilidad de implementar una frecuencia de optimización dinámica basada en la volatilidad del mercado o en el rendimiento reciente del EA:

bool ShouldOptimize()
{
    double recentVolatility = CalculateRecentVolatility();
    int dynamicPeriod = (int)(OptimizationPeriod * (1 + recentVolatility));
    
    return tickCount >= dynamicPeriod;
}

7.2 Equilibrio entre adaptabilidad y estabilidad

Aunque la adaptabilidad es una ventaja clave de los EA autooptimizados, es importante mantener cierto grado de estabilidad. Los cambios drásticos de los parámetros pueden dar lugar a comportamientos comerciales incoherentes.

Considere la posibilidad de poner límites a la cantidad de parámetros que pueden cambiar en una sola optimización:

void LimitParameterChange(int &parameter, int newValue, int maxChange)
{
    int change = newValue - parameter;
    change = MathMax(-maxChange, MathMin(change, maxChange));
    parameter += change;
}

7.3 El sobreajuste es un riesgo importante en cualquier proceso de optimización. Un EA sobreajustado puede funcionar excepcionalmente bien con datos históricos pero fallar cuando se enfrenta a nuevas condiciones del mercado. Para mitigar este riesgo, es importante utilizar una cantidad suficiente de datos históricos para la optimización. Además, implementar pruebas fuera de muestra o una optimización progresiva puede ayudar a garantizar la solidez. También debe considerarse la complejidad de su estrategia en relación con la cantidad de datos disponibles. Monitorear de cerca el desempeño en vivo es crucial, y usted debe estar preparado para intervenir si el comportamiento del EA difiere significativamente de los resultados del backtest.

7.4 La autooptimización puede ser computacionalmente intensiva, por lo que garantizar la eficiencia computacional es clave. Para mantener su EA receptivo, es recomendable ejecutar optimizaciones fuera del horario laboral o en un hilo separado. Las estructuras de datos y algoritmos eficientes pueden reducir en gran medida el tiempo de procesamiento. Para tareas de optimización más intensivas, las soluciones basadas en la nube pueden ser una buena opción a considerar.


8. El desarrollo de un EA con optimización automática presenta nuevos desafíos en la depuración y la resolución de problemas.

8.1 Durante el desarrollo pueden surgir algunos problemas comunes. Por ejemplo, si su EA produce resultados incoherentes en cada ejecución, asegúrese de que está utilizando datos coherentes y compruebe si hay elementos aleatorios en su código. Los malos resultados en directo, a pesar de los buenos resultados de las pruebas retrospectivas, podrían deberse a factores como el deslizamiento, el diferencial o las condiciones cambiantes del mercado. Si la optimización falla o produce resultados inesperados, es importante revisar los criterios de optimización y asegurarse de que la función TestParameters() funciona como se espera.

8.2 Para ayudar con la depuración, es muy recomendable implementar un registro completo. Esto le permitirá realizar un seguimiento detallado del comportamiento del EA y del proceso de optimización, lo que le ayudará a identificar y resolver los problemas con mayor eficacia.

Implemente un registro exhaustivo para realizar un seguimiento del comportamiento del EA y del proceso de optimización:
void Log(string message)
{
    Print(TimeToString(TimeCurrent()) + ": " + message);
    
    int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT);
    if(handle != INVALID_HANDLE)
    {
        FileSeek(handle, 0, SEEK_END);
        FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n");
        FileClose(handle);
    }
}

Utilice esta función para registrar eventos importantes, cambios de parámetros y cualquier error que se produzca durante el funcionamiento o la optimización.

8.3 El Probador de Estrategias de MetaTrader es una herramienta inestimable para depurar su EA. Puede utilizar el modo visual para recorrer el comportamiento de su EA barra a barra, lo que proporciona una visión detallada de cómo está funcionando el EA en tiempo real. Además, comparar los resultados de la optimización en el Probador de Estrategias con los de la optimización automática de su EA puede ayudar a identificar cualquier discrepancia. La función de optimización del Probador de Estrategias también sirve para verificar la funcionalidad de su función TestParameters().



9. Rendimiento y retos del EA de media móvil de optimización automática

9.1 Para ilustrar los beneficios potenciales y los retos de la autooptimización, consideremos un caso de estudio hipotético.

Realizamos una prueba retrospectiva de nuestro EA de cruce de medias móviles con optimización automática en datos H1 del EURUSD que abarcan desde 2010 hasta 2020. El EA se configuró para optimizar sus parámetros cada 5000 ticks. Los resultados fueron prometedores, mostrando un beneficio neto total de 15.420 dólares, un factor de beneficio de 1,65 y una reducción máxima de 2.105 dólares en un total de 1.247 operaciones.

We then compared these results with the same EA using static parameters (MA_Fast_Period = 10, MA_Slow_Period = 20). La versión estática produjo un beneficio neto total de 8.750 $, un factor de beneficio de 1,38 y una reducción máxima más alta de 3.210 $ de 1.562 operaciones totales. Esta comparación demostró que la versión de autooptimización mejoraba significativamente tanto la rentabilidad global como los rendimientos ajustados al riesgo.

9.2 Aunque los resultados del backtest son prometedores, hay que tener en cuenta varios factores importantes a la hora de evaluar el rendimiento en el mundo real. Los cambios en el régimen de mercado, como la crisis financiera de 2008 o la pandemia COVID-19 de 2020, podrían afectar a la capacidad de adaptación de la EA, lo que requiere una cuidadosa evaluación. Además, se deben tener en cuenta los costos de transacción para garantizar que el mejor desempeño justifique cualquier aumento de la actividad comercial. Los recursos computacionales necesarios para ejecutar la optimización continua en un entorno en vivo son otro factor a considerar, como también lo es la resiliencia psicológica necesaria para hacer frente a períodos de bajo rendimiento mientras el EA se adapta a nuevas condiciones.


10. Tendencias futuras en la optimización automática de las EA: Aprendizaje automático, datos externos y soluciones en la nube

10.1 A medida que la tecnología comercial continúa evolucionando, están surgiendo desarrollos interesantes para los EA con optimización automática. Uno de estos avances es la integración de algoritmos de aprendizaje automático en el comercio de Forex, lo que podría mejorar el proceso de optimización al identificar patrones complejos en los datos del mercado. Esto presenta oportunidades para estrategias comerciales aún más adaptables y eficientes en el futuro.

Los algoritmos de aprendizaje automático pueden mejorar potencialmente el proceso de optimización al identificar patrones complejos en los datos del mercado:

from sklearn.ensemble import RandomForestRegressor

def ml_optimize(data, labels):
    model = RandomForestRegressor(n_estimators=100)
    model.fit(data, labels)
    return model.feature_importances_

Si bien este ejemplo utiliza Python, se pueden implementar técnicas de aprendizaje automático similares en MQL5 o integrarse a través de bibliotecas externas.

10.2 Integración con fuentes de datos externas

La incorporación de datos externos (indicadores económicos, análisis de opiniones, etc.) al proceso de optimización puede proporcionar una visión más completa de las condiciones del mercado:

string GetExternalData()
{
    string cookie=NULL,headers;
    char post[],result[];
    int res;
    
    string url="https://api.example.com/economic-data";
    
    res=WebRequest("GET",url,cookie,NULL,500,post,0,result,headers);
    
    if(res==-1)
    {
        Print("Error in WebRequest. Error code  =",GetLastError());
        return "";
    }
    
    string resultString=CharArrayToString(result);
    return resultString;
}

Al integrar datos externos, su EA puede potencialmente tomar decisiones más informadas sobre cuándo y cómo optimizar sus parámetros.

10.3 Optimización basada en la nube

A medida que las tareas de optimización se vuelven más complejas, las soluciones basadas en la nube ofrecen la posibilidad de realizar procesos de optimización más potentes y flexibles:

void CloudOptimize()
{
    string optimizationData = PrepareOptimizationData();
    string url = "https://your-cloud-service.com/optimize";
    string headers = "Content-Type: application/json\r\n";
    char post[], result[];
    string resultHeaders;
    
    StringToCharArray(optimizationData, post);
    
    int res = WebRequest("POST", url, headers, 30000, post, result, resultHeaders);
    
    if(res == -1)
    {
        Print("Error in WebRequest. Error code =", GetLastError());
        return;
    }
    
    string optimizationResult = CharArrayToString(result);
    ApplyCloudOptimizationResult(optimizationResult);
}

Este enfoque permite aprovechar más potencia de cálculo y algoritmos de optimización potencialmente más sofisticados de lo que sería factible en una máquina local.

El desarrollo de una EA autooptimizadora eficaz es un proceso continuo que requiere una mejora y un aprendizaje continuos. Una estrategia de mejora consiste en revisar periódicamente los resultados del EA. Esta práctica ayudará a identificar áreas de mejora y a garantizar que el EA siga funcionando de forma óptima en condiciones de mercado cambiantes.

void WeeklyPerformanceReview()
{
    datetime startOfWeek = iTime(_Symbol, PERIOD_W1, 0);
    double weeklyProfit = 0;
    int totalTrades = 0;
    
    for(int i = 0; i < HistoryDealsTotal(); i++)
    {
        ulong ticket = HistoryDealGetTicket(i);
        if(HistoryDealGetInteger(ticket, DEAL_TIME) >= startOfWeek)
        {
            weeklyProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT);
            totalTrades++;
        }
    }
    
    Print("Weekly Performance: Profit = ", weeklyProfit, ", Trades = ", totalTrades);
}

Utilice estas revisiones para identificar áreas de mejora y posibles problemas en su proceso de optimización.

Mantenerse informado sobre la tecnología comercial, la dinámica del mercado y las regulaciones es esencial. Es fundamental realizar pruebas exhaustivas, incluidas pruebas fuera de muestra y pruebas directas. El aprendizaje y el seguimiento continuos son claves para el éxito a largo plazo. Si bien es potente, la optimización automática debe complementar los principios comerciales sólidos y la gestión de riesgos. Manténgase curioso y cauteloso, priorizando la protección de su capital.

¡Feliz trading!

Para aquellos que buscan profundizar su comprensión de la optimización automática y el comercio algorítmico, aquí hay algunos recursos adicionales:

  1. Building Reliable Trading Systems por Keith Fitschen
  2. Algorithmic Trading: Winning Strategies and Their Rationale por Ernie Chan
  3. Machine Learning for Algorithmic Trading por Stefan Jansen
  4. Documentación MQL5: Documentación MQL5
  5. Foro de Forex Factory - Foro de codificación: Foro Forex Factory
  6. Cursos de Quantopian: Cursos de Quantopian


11. Código EA de ejemplo

A continuación se muestra el código completo para el EA de optimización automática. Copie este código en su MetaEditor y compílelo para usarlo en MetaTrader 5.

//+------------------------------------------------------------------+
//|                 Auto-Optimizing Moving Average Crossover EA      |
//| Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera        |
//|               https://www.mql5.com/en/users/jsgaston/news        |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera"
#property link      "https://www.mql5.com/en/users/jsgaston/news"
#property version   "1.00"
#property strict

// Include necessary libraries
#include <Trade\Trade.mqh>
#include <Arrays\ArrayObj.mqh>

// Input parameters
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method
input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price
input double LotSize = 0.01;        // Lot Size
input int StopLoss = 100;           // Stop Loss in points
input int TakeProfit = 200;        // Take Profit in points
input int Initial_MA_Fast_Period = 10;  // Initial Fast Moving Average Period
input int Initial_MA_Slow_Period = 20;  // Initial Slow Moving Average Period

// Optimization parameters
input bool AutoOptimize = true;    // Enable Auto Optimization
input int OptimizationPeriod = 5000; // Number of ticks between optimizations
input int MinDataPoints = 1000;    // Minimum number of data points for optimization

// Global variables
CTrade trade;
int fastMA_Handle, slowMA_Handle;
double fastMA[], slowMA[];
int tickCount = 0;
CArrayObj optimizationResults;
int MA_Fast_Period, MA_Slow_Period;

// Optimization ranges
const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1;
const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1;

// Class to hold optimization results
class OptimizationResult : public CObject
{
public:
    int fastPeriod;
    int slowPeriod;
    double profit;
};

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    MA_Fast_Period = Initial_MA_Fast_Period;
    MA_Slow_Period = Initial_MA_Slow_Period;

    // Initialize MA handles
    fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
    slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
    
    if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators");
        return INIT_FAILED;
    }
    
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Release MA handles
    IndicatorRelease(fastMA_Handle);
    IndicatorRelease(slowMA_Handle);
    
    // Clean up optimization results
    optimizationResults.Clear();
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Check if we have enough bars to calculate MAs
    if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return;
    
    // Copy MA values
    if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return;
    if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return;
    
    // Auto Optimization
    if(AutoOptimize && ++tickCount >= OptimizationPeriod)
    {
        Optimize();
        tickCount = 0;
    }
    
    // Trading logic
    if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0])
    {
        // Open buy position
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point);
    }
    else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0])
    {
        // Open sell position
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point);
    }
}

//+------------------------------------------------------------------+
//| Optimization function                                            |
//+------------------------------------------------------------------+
void Optimize()
{
    Print("Starting optimization...");
    
    optimizationResults.Clear();
    
    // Loop through all combinations of MA periods
    for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
    {
        for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
        {
            if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
            
            double profit = TestParameters(fastPeriod, slowPeriod);
            
            OptimizationResult* result = new OptimizationResult();
            result.fastPeriod = fastPeriod;
            result.slowPeriod = slowPeriod;
            result.profit = profit;
            
            optimizationResults.Add(result);
        }
    }
    
    // Find the best result
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
        // Update the EA parameters
        MA_Fast_Period = bestResult.fastPeriod;
        MA_Slow_Period = bestResult.slowPeriod;
        
        // Update indicator handles
        IndicatorRelease(fastMA_Handle);
        IndicatorRelease(slowMA_Handle);
        fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
        slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
        
        Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period);
    }
    else
    {
        Print("Optimization failed to find better parameters.");
    }
}

//+------------------------------------------------------------------+
//| Test a set of parameters                                         |
//+------------------------------------------------------------------+
double TestParameters(int fastPeriod, int slowPeriod)
{
    int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
    int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);
    
    if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators for testing");
        return -DBL_MAX;
    }
    
    double fastBuffer[], slowBuffer[];
    ArraySetAsSeries(fastBuffer, true);
    ArraySetAsSeries(slowBuffer, true);
    
    int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer);
    copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer));
    
    if(copied < MinDataPoints)
    {
        Print("Not enough data for testing");
        return -DBL_MAX;
    }
    
    double Close[], Open[];
    ArraySetAsSeries(Close, true);
    ArraySetAsSeries(Open, true);
    copied = CopyClose(_Symbol, PERIOD_CURRENT, 0, copied, Close);
    copied = MathMin(copied, CopyOpen(_Symbol, PERIOD_CURRENT, 0, copied, Open));
    
    double profit = 0;
    for(int i = 1; i < copied; i++)
    {
        if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
        {
            // Buy signal
            profit += Close[i-1] - Open[i];
        }
        else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
        {
            // Sell signal
            profit += Open[i] - Close[i-1];
        }
    }
    
    IndicatorRelease(maFast);
    IndicatorRelease(maSlow);
    
    return profit;
}

//+------------------------------------------------------------------+
//| Custom function to log important events                          |
//+------------------------------------------------------------------+
void Log(string message)
{
    Print(TimeToString(TimeCurrent()) + ": " + message);
    
    int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT);
    if(handle != INVALID_HANDLE)
    {
        FileSeek(handle, 0, SEEK_END);
        FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n");
        FileClose(handle);
    }
}

Cómo utilizar este asesor experto

  1. Copie el código completo en un nuevo archivo en MetaEditor.
  2. Guarde el archivo con una extensión .mq5 (por ejemplo, "AutoOptimizingMA.mq5").
  3. Compila el EA haciendo clic en el botón "Compilar" o presionando F7.
  4. En MetaTrader 5, arrastre el EA compilado a un gráfico.
  5. Ajuste los parámetros de entrada según sea necesario en la ventana de configuración del EA.
  6. Habilite AutoTrading y deje que el EA se ejecute.

Características principales de este EA

  1. Estrategia de cruce de medias móviles: El EA utiliza una estrategia básica de cruce de medias móviles para tomar decisiones de trading.
  2. Optimización automática: El EA puede optimizar automáticamente sus parámetros (períodos de MA rápida y lenta) en función de datos recientes del mercado.
  3. Entradas personalizables: Los usuarios pueden ajustar varios parámetros, incluyendo el tamaño del lote, stop loss, take profit y ajustes de optimización.
  4. Registro del rendimiento: El EA incluye una función de registro para rastrear eventos importantes y cambios de parámetros.

Notas importantes

  • Este EA se proporciona como un ejemplo educativo y no debe utilizarse para operaciones en vivo sin realizar pruebas y personalizaciones exhaustivas. La optimización automática puede requerir un uso intensivo de recursos computacionales. Tenga cuidado con los recursos del sistema, especialmente cuando se ejecuta en un VPS o máquina local. 
  • Siempre pruebe el EA extensivamente en un entorno de demostración antes de considerar el comercio en vivo.
  • Los resultados pasados no garantizan los resultados futuros. Las condiciones del mercado pueden cambiar, afectando potencialmente al rendimiento de la EA.

Al utilizar este EA, puede explorar cómo funciona la autooptimización en la práctica y mejorar potencialmente la adaptabilidad de su estrategia de negociación a las condiciones cambiantes del mercado. Acuérdate de controlar continuamente su rendimiento y hacer los ajustes necesarios.

Ajustes


Entradas

Gráfico


Pruebas retrospectivas

Probablemente puedas obtener mejores resultados agregando más condiciones a las órdenes, como agregar Deep Learning o un RSI, o cualquier cosa que se te ocurra.

Recuerde, el mundo del trading algorítmico es vasto y está en constante evolución. Esta guía sirve como punto de partida en su viaje hacia la optimización automática de Asesores Expertos. A medida que gane experiencia y profundice su comprensión, sin duda descubrirá nuevas técnicas y enfoques para refinar y mejorar sus sistemas de negociación.

¡Buena suerte y que tus operaciones sean rentables!

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

Archivos adjuntos |
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Ejemplo de Análisis de Redes de Causalidad (CNA), Control Óptimo de Modelos Estocásticos (SMOC) y la Teoría de Juegos de Nash con Aprendizaje Profundo (Deep Learning) Ejemplo de Análisis de Redes de Causalidad (CNA), Control Óptimo de Modelos Estocásticos (SMOC) y la Teoría de Juegos de Nash con Aprendizaje Profundo (Deep Learning)
Agregaremos Deep Learning a esos tres ejemplos que se publicaron en artículos anteriores y compararemos los resultados con los anteriores. El objetivo es aprender cómo agregar DL (Deep Learning) a otro EA.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Características del Wizard MQL5 que debe conocer (Parte 38): Bandas de Bollinger Características del Wizard MQL5 que debe conocer (Parte 38): Bandas de Bollinger
Las bandas de Bollinger son un indicador de envolvente muy común utilizado por muchos traders para colocar y cerrar operaciones manualmente. Examinamos este indicador considerando las diferentes señales posibles que genera, y vemos cómo se podrían poner en uso en un Asesor Experto montado por un asistente.