Creación de un sistema personalizado de detección de regímenes de mercado en MQL5 (Parte 2): Asesor experto
- Introducción
- Construyendo un Asesor Experto Adaptativo
- Consideraciones prácticas y optimización
- Indicador: Regímenes de múltiples marcos temporales
- Evaluación del Asesor Experto Adaptativo: Resultados de las pruebas retrospectivas
- Conclusión
Introducción
En la parte 1 de esta serie, sentamos las bases esenciales para abordar el reto que supone la dinámica cambiante del mercado. Establecimos una base estadística sólida, construimos una clase CMarketRegimeDetector capaz de clasificar objetivamente el comportamiento del mercado y desarrollamos un indicador personalizado (MarketRegimeIndicator) para visualizar estos regímenes directamente en nuestros gráficos. Pasamos de reconocer el problema (el deterioro del rendimiento de las estrategias estáticas en mercados dinámicos), a desarrollar un sistema capaz de identificar el estado predominante del mercado: tendencia (al alza/a la baja), rango o volatilidad (enlace a la parte 1 para referencia).
Sin embargo, identificar simplemente el régimen actual es sólo la mitad de la batalla. El verdadero poder reside en adaptar nuestro enfoque comercial basándonos en este conocimiento. Un detector, por sofisticado que sea, sigue siendo una herramienta analítica hasta que sus conocimientos se traduzcan en decisiones comerciales viables. ¿Qué pasaría si nuestro sistema de trading pudiera cambiar de marcha automáticamente, aplicando una lógica de seguimiento de tendencias cuando éstas son fuertes, pasando a tácticas de reversión a la media en mercados laterales y ajustando los parámetros de riesgo cuando aumenta la volatilidad?
Ésta es precisamente la brecha que vamos a superar en la segunda parte. Sobre la base de las bases establecidas anteriormente, ahora nos centraremos en la aplicación práctica y el perfeccionamiento. En este artículo profundizaremos en:
- Creación de un asesor experto adaptativo (EA): Construiremos un MarketRegimeEA completo que integre nuestro CMarketRegimeDetector para seleccionar y ejecutar automáticamente diferentes estrategias de trading (seguimiento de tendencias, reversión a la media, ruptura) adaptadas al régimen detectado.
- Implementación de la gestión de riesgos específica para cada régimen: El EA demostrará cómo ajustar parámetros como el tamaño del lote, el stop loss y el take profit en función del estado actual del mercado.
- Consideraciones prácticas: Exploraremos aspectos cruciales de la implementación en el mundo real, incluida la optimización de parámetros para diferentes instrumentos y marcos temporales.
- Gestión de transiciones de régimen: Estrategias para gestionar los momentos críticos en los que el mercado pasa de un régimen a otro, garantizando ajustes estratégicos más fluidos.
- Técnicas de integración: Debate sobre cómo incorporar el sistema de detección del régimen de mercado en sus marcos comerciales existentes para mejorar su adaptabilidad.
Al finalizar esta segunda parte, usted no solo comprenderá cómo detectar regímenes de mercado, sino también cómo construir un sistema de trading automatizado que adapte inteligentemente su comportamiento, buscando un desempeño más consistente en las diversas condiciones que presentan los mercados financieros. Transformemos nuestro sistema de detección en una solución comercial verdaderamente adaptativa.
Construyendo un Asesor Experto Adaptativo
En esta sección, crearemos un Asesor Experto que utiliza nuestro Detector de Régimen de Mercado para adaptar su estrategia comercial en función de las condiciones actuales del mercado. Esto demuestra cómo la detección de regímenes se puede integrar en un sistema comercial completo.
El MarketRegimeEA
Nuestro Asesor Experto utilizará diferentes enfoques comerciales para diferentes regímenes de mercado:- En mercados con tendencia se utilizará estrategias de seguimiento de tendencias.
- En mercados en rango se utilizará estrategias de reversión a la media.
- En mercados volátiles se utilizará estrategias de ruptura con tamaños de posición reducidos.
Aquí está la implementación:
// Include the Market Regime Detector #include <MarketRegimeEnum.mqh> #include <MarketRegimeDetector.mqh> // EA input parameters input int LookbackPeriod = 100; // Lookback period for calculations input int SmoothingPeriod = 10; // Smoothing period for regime transitions input double TrendThreshold = 0.2; // Threshold for trend detection (0.1-0.5) input double VolatilityThreshold = 1.5; // Threshold for volatility detection (1.0-3.0) // Trading parameters input double TrendingLotSize = 0.1; // Lot size for trending regimes input double RangingLotSize = 0.05; // Lot size for ranging regimes input double VolatileLotSize = 0.02; // Lot size for volatile regimes input int TrendingStopLoss = 100; // Stop loss in points for trending regimes input int RangingStopLoss = 50; // Stop loss in points for ranging regimes input int VolatileStopLoss = 150; // Stop loss in points for volatile regimes input int TrendingTakeProfit = 200; // Take profit in points for trending regimes input int RangingTakeProfit = 80; // Take profit in points for ranging regimes input int VolatileTakeProfit = 300; // Take profit in points for volatile regimes // Global variables CMarketRegimeDetector *Detector = NULL; int OnBarCount = 0; datetime LastBarTime = 0;
El EA incluye parámetros para configurar tanto la detección del régimen como la estrategia comercial. Tenga en cuenta cómo utilizamos diferentes tamaños de lotes, stop loss y take profit para diferentes regímenes de mercado. Esto permite al EA adaptar su enfoque de gestión de riesgos a las condiciones actuales del mercado.
Inicialización del EA
La función OnInit() crea y configura el detector de régimen de mercado:
int OnInit() { // Create and initialize the Market Regime Detector Detector = new CMarketRegimeDetector(LookbackPeriod, SmoothingPeriod); if(Detector == NULL) { Print("Failed to create Market Regime Detector"); return INIT_FAILED; } // Configure the detector Detector.SetTrendThreshold(TrendThreshold); Detector.SetVolatilityThreshold(VolatilityThreshold); Detector.Initialize(); // Initialize variables OnBarCount = 0; LastBarTime = 0; return INIT_SUCCEEDED; }
Esta función crea y configura el Detector de Régimen de Mercado con los parámetros especificados por el usuario. También inicializa las variables de conteo de barras que usaremos para rastrear nuevas barras.
Procesamiento de ticks del EA
La función OnTick() procesa nuevos datos de precios y ejecuta la estrategia comercial basada en el régimen:
void OnTick() { // Check for new bar datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0); if(currentBarTime == LastBarTime) return; // No new bar LastBarTime = currentBarTime; OnBarCount++; // Wait for enough bars to accumulate if(OnBarCount < LookbackPeriod) { Comment("Accumulating data: ", OnBarCount, " of ", LookbackPeriod, " bars"); return; } // Get price data double close[]; ArraySetAsSeries(close, true); int copied = CopyClose(Symbol(), PERIOD_CURRENT, 0, LookbackPeriod, close); if(copied != LookbackPeriod) { Print("Failed to copy price data: copied = ", copied, " of ", LookbackPeriod); return; } // Process data with the detector if(!Detector.ProcessData(close, LookbackPeriod)) { Print("Failed to process data with Market Regime Detector"); return; } // Get current market regime ENUM_MARKET_REGIME currentRegime = Detector.GetCurrentRegime(); // Display current regime information string regimeText = "Current Market Regime: " + Detector.GetRegimeDescription(); string trendText = "Trend Strength: " + DoubleToString(Detector.GetTrendStrength(), 4); string volatilityText = "Volatility: " + DoubleToString(Detector.GetVolatility(), 4); Comment(regimeText + "\n" + trendText + "\n" + volatilityText); // Execute trading strategy based on market regime ExecuteRegimeBasedStrategy(currentRegime); }Esta función:
- Comprueba si hay una nueva barra para evitar cálculos redundantes.
- Espera hasta que se hayan acumulado suficientes barras para una detección de régimen confiable.
- Recupera los últimos datos de precios.
- Procesa los datos con el detector de régimen de mercado.
- Obtiene el régimen actual del mercado.
- Muestra la información del régimen.
- Ejecuta la estrategia comercial basada en el régimen.
El uso de ArraySetAsSeries(close, true) es importante ya que garantiza que la matriz de precios esté indexada en orden inverso, con el precio más reciente en el índice 0. Esta es la convención de indexación estándar en MQL5 para trabajar con datos de series de tiempo.
Estrategia comercial basada en regímenes
La función ExecuteRegimeBasedStrategy() implementa diferentes enfoques comerciales para diferentes regímenes de mercado:
void ExecuteRegimeBasedStrategy(ENUM_MARKET_REGIME regime) { // Check if we already have open positions if(PositionsTotal() > 0) return; // Don't open new positions if we already have one // Get current market information double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT); // Determine trading parameters based on regime double lotSize = 0.0; int stopLoss = 0; int takeProfit = 0; ENUM_ORDER_TYPE orderType = ORDER_TYPE_BUY; switch(regime) { case REGIME_TRENDING_UP: { lotSize = TrendingLotSize; stopLoss = TrendingStopLoss; takeProfit = TrendingTakeProfit; orderType = ORDER_TYPE_BUY; // Buy in uptrend break; } case REGIME_TRENDING_DOWN: { lotSize = TrendingLotSize; stopLoss = TrendingStopLoss; takeProfit = TrendingTakeProfit; orderType = ORDER_TYPE_SELL; // Sell in downtrend break; } case REGIME_RANGING: { // In ranging markets, we can use mean-reversion strategies // For simplicity, we'll use RSI to determine overbought/oversold double rsi[]; ArraySetAsSeries(rsi, true); int rsiCopied = CopyBuffer(iRSI(Symbol(), PERIOD_CURRENT, 14, PRICE_CLOSE), 0, 0, 2, rsi); if(rsiCopied != 2) return; lotSize = RangingLotSize; stopLoss = RangingStopLoss; takeProfit = RangingTakeProfit; if(rsi[0] < 30) // Oversold orderType = ORDER_TYPE_BUY; else if(rsi[0] > 70) // Overbought orderType = ORDER_TYPE_SELL; else return; // No signal break; } case REGIME_VOLATILE: { // In volatile markets, we can use breakout strategies // For simplicity, we'll use Bollinger Bands double upper[], lower[]; ArraySetAsSeries(upper, true); ArraySetAsSeries(lower, true); int bbCopied1 = CopyBuffer(iBands(Symbol(), PERIOD_CURRENT, 20, 2, 0, PRICE_CLOSE), 1, 0, 2, upper); int bbCopied2 = CopyBuffer(iBands(Symbol(), PERIOD_CURRENT, 20, 2, 0, PRICE_CLOSE), 2, 0, 2, lower); if(bbCopied1 != 2 || bbCopied2 != 2) return; lotSize = VolatileLotSize; stopLoss = VolatileStopLoss; takeProfit = VolatileTakeProfit; double close[]; ArraySetAsSeries(close, true); int copied = CopyClose(Symbol(), PERIOD_CURRENT, 0, 2, close); if(copied != 2) return; if(close[1] < upper[1] && close[0] > upper[0]) // Breakout above upper band orderType = ORDER_TYPE_BUY; else if(close[1] > lower[1] && close[0] < lower[0]) // Breakout below lower band orderType = ORDER_TYPE_SELL; else return; // No signal break; } default: return; // No trading in undefined regime } // Calculate stop loss and take profit levels double slLevel = 0.0; double tpLevel = 0.0; if(orderType == ORDER_TYPE_BUY) { slLevel = ask - stopLoss * point; tpLevel = ask + takeProfit * point; } else if(orderType == ORDER_TYPE_SELL) { slLevel = bid + stopLoss * point; tpLevel = bid - takeProfit * point; } // Execute trade MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = lotSize; request.type = orderType; request.price = (orderType == ORDER_TYPE_BUY) ? ask : bid; request.sl = slLevel; request.tp = tpLevel; request.deviation = 10; request.magic = 123456; // Magic number for this EA request.comment = "Market Regime: " + Detector.GetRegimeDescription(); request.type_filling = ORDER_FILLING_FOK; bool success = OrderSend(request, result); if(success) { Print("Trade executed successfully: ", result.retcode, " ", result.comment); } else { Print("Trade execution failed: ", result.retcode, " ", result.comment); } }Esta función implementa una estrategia comercial integral basada en regímenes:
- Rregímenes de tendencia: Utiliza estrategias de seguimiento de tendencias, comprando en tendencias alcistas y vendiendo en tendencias bajistas.
- Regímenes de rango: Utiliza estrategias de reversión a la media basadas en el RSI, comprando cuando hay sobreventa y vendiendo cuando hay sobrecompra.
- Regímenes de volatilidad: Utiliza estrategias de ruptura basadas en las Bandas de Bollinger, con posiciones reducidas para gestionar el riesgo.
El uso de diferentes estrategias para diferentes regímenes es la innovación clave de este EA. Al adaptar su enfoque a las condiciones actuales del mercado, el EA puede lograr un rendimiento más consistente en una amplia gama de entornos de mercado.
Limpieza del EA
La función OnDeinit() garantiza una limpieza adecuada cuando se elimina el EA:
void OnDeinit(const int reason) { // Clean up if(Detector != NULL) { delete Detector; Detector = NULL; } // Clear the comment Comment(""); }
Esta función elimina el objeto detector de régimen de mercado para evitar pérdidas de memoria y borra cualquier comentario del gráfico.
Ventajas del comercio basado en regímenes
Este asesor experto demuestra varias ventajas clave del trading basado en regímenes:- Adaptabilidad: El EA adapta automáticamente su estrategia comercial a las condiciones actuales del mercado, utilizando diferentes enfoques para diferentes regímenes.
- Gestión de riesgos: El EA ajusta el tamaño de las posiciones en función de la volatilidad del mercado, utilizando posiciones más pequeñas en regímenes más volátiles para gestionar el riesgo.
- Selección de estrategias: El EA selecciona la estrategia más adecuada para cada régimen, utilizando el seguimiento de tendencias en mercados con tendencia y la reversión a la media en mercados con oscilaciones.
- Transparencia: El EA muestra claramente el régimen actual del mercado y sus características clave, lo que proporciona a los operadores un contexto valioso para sus decisiones comerciales.
Al incorporar la detección del régimen de mercado en sus sistemas de negociación, puede obtener beneficios similares, creando estrategias más sólidas y adaptables que funcionan bien en una amplia gama de condiciones de mercado.
En la siguiente sección, analizaremos consideraciones prácticas para implementar y optimizar el sistema de detección de regímenes de mercado en entornos comerciales reales.
Consideraciones prácticas y optimización
En esta sección final, discutiremos consideraciones prácticas para implementar y optimizar el Sistema de Detección del Régimen de Mercado en entornos comerciales reales. Cubriremos la optimización de parámetros, el manejo de la transición de régimen y la integración con los sistemas comerciales existentes.
Parámetros de optimización
La eficacia de nuestro sistema de detección del régimen de mercado depende en gran medida de la elección de los parámetros. A continuación se presentan los parámetros clave que los traders deben optimizar para sus instrumentos comerciales y marcos temporales específicos:
- Período de retroceso (lookback):El período de retroceso determina la cantidad de datos históricos que se utilizan para la detección de regímenes. Los períodos más largos proporcionan clasificaciones de régimen más estables pero pueden ser menos sensibles a los cambios recientes del mercado. Los períodos más cortos son más sensibles pero pueden generar más señales falsas.
// Example of testing different lookback periods for(int lookback = 50; lookback <= 200; lookback += 25) { CMarketRegimeDetector detector(lookback, SmoothingPeriod); detector.SetTrendThreshold(TrendThreshold); detector.SetVolatilityThreshold(VolatilityThreshold); // Process historical data and evaluate performance // ... }
Por lo general, los períodos de retrospección entre 50 y 200 barras funcionan bien para la mayoría de los instrumentos y períodos de tiempo. El valor óptimo depende de la duración típica de los regímenes de mercado para el instrumento específico que se negocia. - Umbral de tendencia: El umbral de tendencia determina la intensidad que debe tener una tendencia para clasificar el mercado como tendencial. Los umbrales más altos dan como resultado menos clasificaciones de tendencias pero con mayor confianza. Los umbrales más bajos identifican más tendencias pero pueden incluir otras más débiles.
// Example of testing different trend thresholds for(double threshold = 0.1; threshold <= 0.5; threshold += 0.05) { CMarketRegimeDetector detector(LookbackPeriod, SmoothingPeriod); detector.SetTrendThreshold(threshold); detector.SetVolatilityThreshold(VolatilityThreshold); // Process historical data and evaluate performance // ... }
Los umbrales de tendencia entre 0,1 y 0,3 son puntos de partida comunes. El valor óptimo depende del comportamiento de tendencia típico del instrumento. - Umbral de volatilidad: El umbral de volatilidad determina cuánta volatilidad se requiere para clasificar al mercado como volátil. Los umbrales más altos dan como resultado menos clasificaciones volátiles, mientras que los umbrales más bajos identifican períodos más volátiles.
// Example of testing different volatility thresholds for(double threshold = 1.0; threshold <= 3.0; threshold += 0.25) { CMarketRegimeDetector detector(LookbackPeriod, SmoothingPeriod); detector.SetTrendThreshold(TrendThreshold); detector.SetVolatilityThreshold(threshold); // Process historical data and evaluate performance // ... }
Los umbrales de volatilidad entre 1,5 y 2,5 son puntos de partida comunes. El valor óptimo depende de las características de volatilidad típicas del instrumento.
Manejo de transiciones de régimen
Nota: Los siguientes 5 bloques de código no son la implementación de la idea, sino solo las ideas y el pseudocódigo de cómo podría verse la implementación.
Las transiciones de régimen son momentos críticos que requieren atención especial. Los cambios abruptos en la estrategia comercial durante las transiciones pueden generar una mala ejecución y un mayor deslizamiento. Aquí hay estrategias para manejar las transiciones de manera más efectiva
- Transiciones suavizadas: El parámetro del período de suavizado ayuda a reducir el ruido de la clasificación de regímenes al exigir que un régimen persista durante un número mínimo de barras antes de ser reconocido:
// Example of implementing smoothed regime transitions ENUM_MARKET_REGIME SmoothRegimeTransition(ENUM_MARKET_REGIME newRegime) { static ENUM_MARKET_REGIME regimeHistory[20]; static int historyCount = 0; // Add new regime to history for(int i = 19; i > 0; i--) regimeHistory[i] = regimeHistory[i-1]; regimeHistory[0] = newRegime; if(historyCount < 20) historyCount++; // Count occurrences of each regime int regimeCounts[5] = {0}; for(int i = 0; i < historyCount; i++) regimeCounts[regimeHistory[i]]++; // Find most common regime int maxCount = 0; ENUM_MARKET_REGIME dominantRegime = REGIME_UNDEFINED; for(int i = 0; i < 5; i++) { if(regimeCounts[i] > maxCount) { maxCount = regimeCounts[i]; dominantRegime = (ENUM_MARKET_REGIME)i; } } return dominantRegime; }
Esta función mantiene un historial de clasificaciones de regímenes recientes y devuelve el régimen más común, reduciendo el impacto de las fluctuaciones temporales. - Ajuste gradual del tamaño de las posiciones: Durante las transiciones de régimen, suele ser prudente ajustar el tamaño de las posiciones de forma gradual en lugar de realizar cambios bruscos:
// Example of gradual position sizing during transitions double CalculateTransitionLotSize(ENUM_MARKET_REGIME previousRegime, ENUM_MARKET_REGIME currentRegime, int transitionBars, int maxTransitionBars) { // Base lot sizes for each regime double regimeLotSizes[5] = { TrendingLotSize, // REGIME_TRENDING_UP TrendingLotSize, // REGIME_TRENDING_DOWN RangingLotSize, // REGIME_RANGING VolatileLotSize, // REGIME_VOLATILE 0.0 // REGIME_UNDEFINED }; // If not in transition, use current regime's lot size if(previousRegime == currentRegime || transitionBars >= maxTransitionBars) return regimeLotSizes[currentRegime]; // Calculate weighted average during transition double previousWeight = (double)(maxTransitionBars - transitionBars) / maxTransitionBars; double currentWeight = (double)transitionBars / maxTransitionBars; return regimeLotSizes[previousRegime] * previousWeight + regimeLotSizes[currentRegime] * currentWeight; }
Esta función calcula un promedio ponderado de los tamaños de posiciones durante las transiciones de régimen, proporcionando un ajuste más suave a medida que cambia el mercado.
Integración con sistemas comerciales existentes
El sistema de detección del régimen de mercado se puede integrar con los sistemas comerciales existentes para mejorar su rendimiento. A continuación se presentan estrategias para una integración efectiva:
- Selección de estrategia: Utilice el régimen detectado para seleccionar la estrategia comercial más adecuada:
// Example of strategy selection based on market regime bool ExecuteTradeSignal(ENUM_MARKET_REGIME regime, int strategySignal) { // Strategy signal: 1 = buy, -1 = sell, 0 = no signal switch(regime) { case REGIME_TRENDING_UP: case REGIME_TRENDING_DOWN: // In trending regimes, only take signals in the direction of the trend if((regime == REGIME_TRENDING_UP && strategySignal == 1) || (regime == REGIME_TRENDING_DOWN && strategySignal == -1)) return true; break; case REGIME_RANGING: // In ranging regimes, take all signals if(strategySignal != 0) return true; break; case REGIME_VOLATILE: // In volatile regimes, be more selective // Only take strong signals (implementation depends on strategy) if(IsStrongSignal(strategySignal)) return true; break; default: // In undefined regimes, don't trade break; } return false; }
Esta función filtra las señales comerciales en función del régimen actual del mercado y ejecuta únicamente operaciones que se alinean con las características del régimen. - Adaptación de parámetros: Adaptar los parámetros de la estrategia en función del régimen detectado:
// Example of parameter adaptation based on market regime void AdaptStrategyParameters(ENUM_MARKET_REGIME regime) { switch(regime) { case REGIME_TRENDING_UP: case REGIME_TRENDING_DOWN: // In trending regimes, use longer moving averages FastPeriod = 20; SlowPeriod = 50; // Use wider stop losses StopLoss = TrendingStopLoss; // Use larger take profits TakeProfit = TrendingTakeProfit; break; case REGIME_RANGING: // In ranging regimes, use shorter moving averages FastPeriod = 10; SlowPeriod = 25; // Use tighter stop losses StopLoss = RangingStopLoss; // Use smaller take profits TakeProfit = RangingTakeProfit; break; case REGIME_VOLATILE: // In volatile regimes, use very short moving averages FastPeriod = 5; SlowPeriod = 15; // Use wider stop losses StopLoss = VolatileStopLoss; // Use larger take profits TakeProfit = VolatileTakeProfit; break; default: // In undefined regimes, use default parameters FastPeriod = 14; SlowPeriod = 28; StopLoss = 100; TakeProfit = 200; break; } }
Esta función ajusta los parámetros de la estrategia en función del régimen actual del mercado, optimizando la estrategia para las condiciones específicas del mercado.
Monitoreo del rendimiento
Supervise periódicamente el rendimiento de su sistema de detección de regímenes de mercado para garantizar que clasifique con precisión los regímenes de mercado:
// Example of performance monitoring for regime detection void MonitorRegimeDetectionPerformance() { static int regimeTransitions = 0; static int correctPredictions = 0; static ENUM_MARKET_REGIME lastRegime = REGIME_UNDEFINED; // Get current regime ENUM_MARKET_REGIME currentRegime = Detector.GetCurrentRegime(); // If regime has changed, evaluate the previous regime's prediction if(currentRegime != lastRegime && lastRegime != REGIME_UNDEFINED) { regimeTransitions++; // Evaluate if the previous regime's prediction was correct // Implementation depends on your specific evaluation criteria if(EvaluateRegimePrediction(lastRegime)) correctPredictions++; // Log performance metrics double accuracy = (double)correctPredictions / regimeTransitions * 100.0; Print("Regime Detection Accuracy: ", DoubleToString(accuracy, 2), "% (", correctPredictions, "/", regimeTransitions, ")"); } lastRegime = currentRegime; }
Esta función rastrea las transiciones de régimen y evalúa la precisión de las predicciones del régimen, proporcionando información valiosa para la optimización del sistema.
Indicador: Regímenes de múltiples marcos temporales
Código completo:
#property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 // Include the Market Regime Detector #include <MarketRegimeEnum.mqh> #include <MarketRegimeDetector.mqh> // Input parameters input int LookbackPeriod = 100; // Lookback period for calculations input double TrendThreshold = 0.2; // Threshold for trend detection (0.1-0.5) input double VolatilityThreshold = 1.5; // Threshold for volatility detection (1.0-3.0) // Timeframes to analyze input bool UseM1 = false; // Use 1-minute timeframe input bool UseM5 = false; // Use 5-minute timeframe input bool UseM15 = true; // Use 15-minute timeframe input bool UseM30 = true; // Use 30-minute timeframe input bool UseH1 = true; // Use 1-hour timeframe input bool UseH4 = true; // Use 4-hour timeframe input bool UseD1 = true; // Use Daily timeframe input bool UseW1 = false; // Use Weekly timeframe input bool UseMN1 = false; // Use Monthly timeframe // Global variables CMarketRegimeDetector *Detectors[]; ENUM_TIMEFRAMES Timeframes[]; int TimeframeCount = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize timeframes array InitializeTimeframes(); // Create detectors for each timeframe ArrayResize(Detectors, TimeframeCount); for(int i = 0; i < TimeframeCount; i++) { Detectors[i] = new CMarketRegimeDetector(LookbackPeriod); if(Detectors[i] == NULL) { Print("Failed to create Market Regime Detector for timeframe ", EnumToString(Timeframes[i])); return INIT_FAILED; } // Configure the detector Detectors[i].SetTrendThreshold(TrendThreshold); Detectors[i].SetVolatilityThreshold(VolatilityThreshold); Detectors[i].Initialize(); } // Set indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Multi-Timeframe Regime Analysis"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Initialize timeframes array based on user inputs | //+------------------------------------------------------------------+ void InitializeTimeframes() { // Count selected timeframes TimeframeCount = 0; if(UseM1) TimeframeCount++; if(UseM5) TimeframeCount++; if(UseM15) TimeframeCount++; if(UseM30) TimeframeCount++; if(UseH1) TimeframeCount++; if(UseH4) TimeframeCount++; if(UseD1) TimeframeCount++; if(UseW1) TimeframeCount++; if(UseMN1) TimeframeCount++; // Resize and fill timeframes array ArrayResize(Timeframes, TimeframeCount); int index = 0; if(UseM1) Timeframes[index++] = PERIOD_M1; if(UseM5) Timeframes[index++] = PERIOD_M5; if(UseM15) Timeframes[index++] = PERIOD_M15; if(UseM30) Timeframes[index++] = PERIOD_M30; if(UseH1) Timeframes[index++] = PERIOD_H1; if(UseH4) Timeframes[index++] = PERIOD_H4; if(UseD1) Timeframes[index++] = PERIOD_D1; if(UseW1) Timeframes[index++] = PERIOD_W1; if(UseMN1) Timeframes[index++] = PERIOD_MN1; } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { // Check if there's enough data if(rates_total < LookbackPeriod) return 0; // Process data for each timeframe string commentText = "Multi-Timeframe Regime Analysis\n\n"; for(int i = 0; i < TimeframeCount; i++) { // Get price data for this timeframe double tfClose[]; ArraySetAsSeries(tfClose, true); int copied = CopyClose(Symbol(), Timeframes[i], 0, LookbackPeriod, tfClose); if(copied != LookbackPeriod) { Print("Failed to copy price data for timeframe ", EnumToString(Timeframes[i])); continue; } // Process data with the detector if(!Detectors[i].ProcessData(tfClose, LookbackPeriod)) { Print("Failed to process data for timeframe ", EnumToString(Timeframes[i])); continue; } // Add timeframe information to comment commentText += TimeframeToString(Timeframes[i]) + ": "; commentText += Detectors[i].GetRegimeDescription(); commentText += " (Trend: " + DoubleToString(Detectors[i].GetTrendStrength(), 2); commentText += ", Vol: " + DoubleToString(Detectors[i].GetVolatility(), 2) + ")"; commentText += "\n"; } // Display the multi-timeframe analysis Comment(commentText); // Return the number of calculated bars return rates_total; } //+------------------------------------------------------------------+ //| Convert timeframe enum to readable string | //+------------------------------------------------------------------+ string TimeframeToString(ENUM_TIMEFRAMES timeframe) { switch(timeframe) { case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; default: return "Unknown"; } } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Clean up for(int i = 0; i < TimeframeCount; i++) { if(Detectors[i] != NULL) { delete Detectors[i]; Detectors[i] = NULL; } } // Clear the comment Comment(""); }
Este indicador no traza líneas en el gráfico. En su lugar, analiza los regímenes del mercado (tendencia ascendente/descendente, rango, volátil) en múltiples períodos de tiempo seleccionados por el usuario utilizando la clase CMarketRegimeDetector (desarrollada en la parte 1) y muestra los resultados como texto en la esquina superior izquierda del gráfico.
Explicación del código:
-
Propiedades e inclusiones:
- #property indicator_chart_window : Hace que el indicador se ejecute en la ventana principal del gráfico.
- #property indicator_buffers 0 , #property indicator_plots 0 : Especifica que este indicador no utiliza ningún buffer de datos ni líneas de gráfico/histogramas.
- #include <MarketRegimeEnum.mqh> , #include <MarketRegimeDetector.mqh> : Incluye las definiciones necesarias y la clase de detector principal de nuestro trabajo anterior.
-
Parámetros de entrada:
- LookbackPeriod, TrendThreshold, VolatilityThreshold: Son los ajustes básicos para la lógica de detección de regímenes, que se aplican de manera uniforme a todos los marcos temporales analizados.
- UseM1 a UseMN1: Una serie de entradas booleanas que permiten al usuario alternar (verdadero/falso) qué intervalos de tiempo estándar (de 1 minuto a mensual) deben incluirse en el análisis.
-
Variables globales:
- Detectors[]: Una matriz diseñada para contener instancias separadas de CMarketRegimeDetector. Cada período de tiempo seleccionado tendrá su propio objeto detector dedicado almacenado aquí.
- Timeframes[]: Una matriz para almacenar los identificadores de marcos de tiempo MQL5 (como PERIOD_H1, PERIOD_D1) correspondientes a los marcos de tiempo elegidos por el usuario a través de las entradas.
- TimeframeCount: Un número entero para realizar un seguimiento de cuántos períodos de tiempo se seleccionaron realmente.
-
OnInit() (función de inicialización):
- Se ejecuta una vez cuando se inicia el indicador.
- Llama a InitializeTimeframes() para determinar qué períodos de tiempo seleccionó el usuario y completar la matriz Timeframes.
- Redimensiona la matriz Detectores para que coincida con TimeframeCount.
- Bucles TimeframeCount
- Para cada intervalo de tiempo seleccionado, crea un objeto CMarketRegimeDetector nuevo utilizando el LookbackPeriod compartido.
- Configura esta instancia específica del detector con los valores compartidos TrendThreshold y VolatilityThreshold.
- Fundamentalmente, cada instancia de detector mantiene su propio estado interno basado en los datos de su período de tiempo asignado.
- Establece el nombre para mostrar del indicador.
-
InitializeTimeframes() (función auxiliar):
- Cuenta cuántas entradas Use están establecidas en true.
- Redimensiona la matriz de marcos de tiempo globales según corresponda.
- Rellena la matriz de períodos de tiempo con las constantes PERIOD_... reales para los períodos de tiempo seleccionados.
-
OnCalculate() (función de cálculo principal):
- Se ejecuta en cada nuevo tick o barra.
- Comprueba si hay suficientes barras históricas (rates_total) disponibles en el marco temporal actual del gráfico para cumplir con el requisito de LookbackPeriod.
- Inicializa una cadena vacía commentText .
- Recorre cada período de tiempo seleccionado (rastreado por i):
- Utiliza CopyClose() para obtener los últimos precios de cierre de LookbackPeriod específicamente para Symbol() y Timeframes[i] (por ejemplo, obtiene precios de cierre H4 incluso si el indicador está en un gráfico M15).
- Si la obtención de datos se realiza correctamente, llama al método ProcessData() del objeto detector correspondiente ( Detectors[i] ) con los datos de precios obtenidos ( tfClose ).
- Agrega los resultados (nombre del período de tiempo, descripción del régimen, intensidad de la tendencia, volatilidad) de Detectors[i] a la cadena commentText.
- Por último, utiliza Comment(commentText) para mostrar el análisis agregado de todos los períodos de tiempo seleccionados en la esquina del gráfico.
-
TimeframeToString() (función auxiliar):
- Una utilidad sencilla para convertir constantes MQL5 PERIOD_... en cadenas legibles como "M1", "H4", "D1".
- Una utilidad sencilla para convertir constantes MQL5 PERIOD_... en cadenas legibles como "M1", "H4", "D1".
-
OnDeinit() (función de desinicialización):
- Se ejecuta una vez cuando se elimina el indicador del gráfico o se cierra la terminal.
- Recorre la matriz Detectors y utiliza delete para liberar la memoria asignada para cada objeto CMarketRegimeDetector creado en OnInit(), evitando fugas de memoria.
- Borra el comentario de texto de la esquina del gráfico.
En esencia, este código configura de manera eficiente detectores de régimen independientes para múltiples períodos de tiempo, obtiene los datos necesarios para cada uno, realiza el análisis y presenta una descripción general consolidada del régimen de múltiples períodos de tiempo directamente en el gráfico del usuario.

Usando este indicador de marco temporal múltiple mencionado anteriormente, se puede crear otro EA que analice múltiples marcos temporales antes de colocar la operación. Aunque por el bien de la brevedad, no crearemos otro Asesor Experto, sino que deberíamos probar este primero.
Evaluación del Asesor Experto Adaptativo: Resultados de las pruebas retrospectivas
Una vez construido MarketRegimeEA, el siguiente paso lógico es evaluar su rendimiento mediante pruebas retrospectivas. Esto nos permite observar cómo funciona la lógica adaptativa del régimen en datos históricos y evaluar el impacto de la configuración de sus parámetros.
Configuración de prueba inicial
Para esta demostración, seleccionamos el oro (XAUUSD) como instrumento de prueba, utilizando datos en el marco temporal M1. Los parámetros iniciales aplicados al EA fueron elegidos arbitrariamente como:
- LookbackPeriod: 100
- SmoothingPeriod: 10
- TrendThreshold: 0.2
- VolatilityThreshold: 1.5
- Lot Sizes: Trending=0.1, Ranging=0.1, Volatile=0.1
- SL: Trending=1000, Ranging=1600, Volatile=2000
- TP: Trending=1400, Ranging=1000, Volatile=1800

Al ejecutar el EA con estos parámetros predeterminados se obtuvieron los siguientes resultados:

Como observamos en la curva de capital y las métricas de rendimiento, la prueba retrospectiva inicial con estas configuraciones produjo resultados subóptimos. Si bien hubo períodos en los que la estrategia generó ganancias (en un momento dado alcanzó un crecimiento del capital de aproximadamente el 20%), el desempeño general indica una falta de rentabilidad consistente y caídas significativas. Este resultado subraya la importancia de ajustar los parámetros para el instrumento específico y el período de tiempo que se negocia. Estos parámetros sirven como punto de partida, pero no representan la configuración óptima.
Para explorar el potencial de mejorar el rendimiento, empleamos las capacidades de optimización de parámetros dentro del MetaTrader 5 Strategy Tester, utilizando su algoritmo genético. El objetivo era identificar un conjunto de parámetros (dentro del rango probado) que alineen mejor el régimen de detección y la lógica comercial con el comportamiento histórico del precio del oro durante el período de backtest. Los parámetros seleccionados para la optimización incluyeron los valores Stop Loss y Take Profit específicos del régimen con otros parámetros como predeterminados.
Tras el proceso de optimización, el evaluador de estrategias identificó el siguiente conjunto de parámetros como los que producen un rendimiento histórico significativamente mejorado:

Al ejecutar nuevamente la prueba retrospectiva utilizando estos parámetros optimizados, se obtuvo un perfil de rendimiento notablemente diferente:

El backtest que utiliza los parámetros optimizados demuestra una mejora marcada, mostrando una ganancia neta positiva y una curva de capital más favorable en comparación con la ejecución inicial.

Sin embargo, es fundamental interpretar estos resultados con cautela. La optimización de parámetros, especialmente el uso de algoritmos genéticos en datos históricos y la ejecución de pruebas retrospectivas en el mismo período, introduce inherentemente un riesgo de sobreajuste. Esto significa que los parámetros pueden ser excepcionalmente adecuados para los datos históricos específicos utilizados, pero es posible que no se generalicen bien a datos de mercado futuros no vistos.
Por lo tanto, si bien los resultados optimizados muestran la potencial del marco de estrategia adaptativa cuando se ajustan los parámetros, no deben tomarse como prueba definitiva de la rentabilidad futura. El propósito principal de este ejercicio es demostrar:
- La funcionalidad del EA adaptativo al régimen.
- Los parámetros de impacto significativo que tienen sobre el rendimiento.
- Que el concepto subyacente (adaptar la estrategia al régimen) puede producir resultados positivos en datos históricos cuando se configura adecuadamente.
El hecho de que incluso la versión no optimizada mostrara períodos de rentabilidad sugiere que la lógica central no es fundamentalmente errónea. Sin embargo, desarrollar una estrategia robusta y lista para producción requeriría pasos adicionales más allá de la simple optimización, incluidos:
- Pruebas rigurosas fuera de muestra para validar la robustez de los parámetros.
- Incorporando una gestión de riesgos más sofisticada.
- Se pueden implementar potencialmente técnicas analizadas anteriormente, como transiciones de suavizado y dimensionamiento gradual de la posición, para manejar los cambios de régimen de manera más efectiva.
Conclusión
A lo largo de esta serie de dos partes, hemos pasado de identificar un desafío fundamental en el trading algorítmico (el efecto perjudicial de las condiciones cambiantes del mercado en las estrategias estáticas) a diseñar e implementar una solución completa y adaptativa. En la parte 1 construimos el motor para comprender el estado del mercado mediante el desarrollo de un sistema de detección de régimen de mercado basado en estadísticas y la visualización de su resultado.
En esta segunda y última parte dimos el paso crucial de la detección a la acción. Demostramos cómo aprovechar el poder de nuestro CMarketRegimeDetector mediante la construcción de MarketRegimeEA, un asesor experto capaz de cambiar automáticamente entre estrategias de seguimiento de tendencias, reversión a la media y ruptura en función del régimen de mercado identificado. Vimos cómo adaptar no solo la lógica de entrada sino también los parámetros de riesgo como el tamaño del lote y los niveles de stop pueden crear un enfoque comercial más resistente.
Además, abordamos las realidades prácticas de la implementación de un sistema de este tipo. Exploramos la importancia de la optimización de parámetros (período de retrospección, umbral de tendencia, umbral de volatilidad) para ajustar el detector a características específicas del mercado y discutimos técnicas para manejar transiciones de régimen sin problemas, minimizando posibles fluctuaciones o cambios abruptos de estrategia. Por último, abordamos cómo este marco de detección de regímenes se puede integrar en los sistemas comerciales existentes, actuando como un filtro inteligente o un ajustador de parámetros.
El objetivo era ambicioso: crear sistemas comerciales que reconocieran y se adaptaran a la no estacionariedad inherente del mercado. Al combinar las capacidades de detección desarrolladas en la Parte 1 con el marco de ejecución adaptativa y las consideraciones prácticas analizadas en la Parte 2, ahora posee el plan para construir estrategias que sean significativamente más sólidas y se ajusten a la naturaleza dinámica de los mercados financieros. El viaje desde un enfoque estático a uno adaptativo al régimen está completo, lo que le permite navegar por las complejidades del mercado con mayor inteligencia y flexibilidad.
Descripción general del archivo
Aquí hay un resumen de todos los archivos creados en este artículo: | Nombre del archivo | Descripción |
|---|---|
| MarketRegimeEnum.mqh | Define los tipos de enumeración del régimen de mercado utilizados en todo el sistema. |
| CStatistics.mqh | Clase de cálculos estadísticos para la detección del régimen de mercado. |
| MarketRegimeDetector.mqh | Implementación de la detección del régimen central del mercado. |
| MarketRegimeEA.mq5 | Asesor Experto que se adapta a diferentes regímenes de mercado. |
| MultiTimeframeRegimes.mq5 | Ejemplo de análisis de regímenes en múltiples períodos de tiempo. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17781
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Pronosticamos barras Renko con ayuda de IA CatBoost
Análisis espectral singular unidimensional
Particularidades del trabajo con números del tipo double en MQL4
Creación de un sistema personalizado de detección de regímenes de mercado en MQL5 (Parte 1): Indicador
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso