English Русский 中文 Deutsch 日本語
preview
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 13): Herramienta RSI Sentinel

Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 13): Herramienta RSI Sentinel

MetaTrader 5Sistemas comerciales |
153 1
Christian Benjamin
Christian Benjamin

Contenido


Introducción

La divergencia es un concepto del análisis técnico donde el movimiento de un indicador, como el impulso o los osciladores, se desvía del movimiento del precio. Básicamente, cuando el precio forma nuevos máximos o mínimos que no se reflejan en el indicador, puede señalar un debilitamiento de la tendencia y potencialmente presagiar una reversión o un cambio en el impulso. La divergencia del RSI es una forma sencilla de detectar posibles reversiones del mercado. Cuando el precio se mueve en una dirección mientras el RSI va en otra, podría indicar un cambio de tendencia. Sin embargo, escanear sus gráficos manualmente para buscar estas señales puede ser lento y propenso a errores. Ahí es donde entra la automatización.

En este artículo, crearemos un asesor experto MQL5 que detecta automáticamente señales de divergencia RSI. El EA marcará estas señales con flechas claras en su gráfico y proporcionará un breve resumen para que pueda ver rápidamente lo que está sucediendo. Ya sea que sea un principiante o un comerciante experimentado, esta herramienta lo ayuda a detectar oportunidades comerciales que puede validar antes de ejecutar operaciones, todo sin gastar horas en análisis manual. Profundicemos y veamos cómo este EA de divergencia RSI puede simplificar su proceso de trading.


Descripción general de la estrategia

Entendiendo la divergencia del RSI

La divergencia RSI ocurre cuando el índice de fuerza relativa (Relative Strength Index, RSI) se mueve en una dirección diferente al precio del activo, lo que indica un posible cambio en el impulso del precio. Este contraste entre el RSI y la acción del precio es un indicador clave que los traders utilizan para anticipar reversiones del mercado o continuaciones de tendencias. Normalmente, el RSI sigue el impulso del precio, confirmando así las tendencias predominantes. Sin embargo, cuando aparece la divergencia, revela una discrepancia que a menudo precede a un movimiento de precios significativo. Reconocer estas señales de forma temprana puede ser crucial para cronometrar entradas y salidas del mercado.

En el contexto de la divergencia del RSI, hay dos tipos principales

1. Divergencia regular del RSI

La divergencia regular del RSI generalmente se considera una señal de reversión. Indica que la tendencia actual está perdiendo fuerza y podría estar a punto de revertirse.

  • Divergencia alcista regular del RSI

Se produce cuando el precio forma un mínimo más bajo mientras que el RSI forma un mínimo más alto. Esto sugiere que, aunque el precio está bajando, el impulso está empezando a cambiar hacia arriba, lo que indica una posible reversión a una tendencia alcista.

Divergencia alcista

Figura 1. Divergencia alcista

  • Divergencia bajista regular del RSI

Ocurre cuando el precio forma un máximo más alto mientras que el RSI forma un máximo más bajo. A pesar del aumento del precio, el debilitamiento del impulso (como lo muestra el RSI) indica que una recesión podría estar en el horizonte.

Señal de venta

Figura 2. Divergencia bajista

2. Divergencia oculta del RSI

La divergencia oculta del RSI se interpreta como una señal de continuación de la tendencia, en lugar de una reversión inminente. Confirma que la tendencia actual todavía tiene fuerza, incluso cuando el RSI y el precio divergen temporalmente.

  • Divergencia alcista oculta del RSI: en una tendencia alcista, si el precio forma un mínimo más alto mientras que el RSI forma un mínimo más bajo, indica que la corrección es solo temporal y que es probable que la tendencia alcista continúe.

Alcista oculta

Figura 3. Divergencia alcista oculta

  • Divergencia bajista oculta del RSI: en una tendencia bajista, cuando el precio forma un máximo más bajo mientras que el RSI forma un máximo más alto, confirma la fortaleza de la tendencia bajista y sugiere que es probable que el movimiento descendente persista.

Divergencia bajista

Figura 4. Divergencia bajista oculta

A continuación se muestra una tabla resumen que resume las diferencias clave entre los tipos de divergencia RSI:

Tipo de divergencia del RSI Acción del precio Acción RSI Tipo de señal Expectativa
Alcista regular Low Low(LL) Higher Low(HL) Reversión al alza De tendencia bajista a tendencia alcista
Bajista regular Higher High(HH)  Lower Higher(LH) Reversión a la baja De tendencia alcista a tendencia bajista
Alcista oculta High Low(HL) Low Low(LL) Continuación hacia arriba La tendencia alcista continúa
Bajista oculto Lower High (LH) High High(HH) Continuación hacia abajo La tendencia bajista continúa

En resumen, este EA escanea continuamente los datos de precios y RSI durante un período retrospectivo definido para detectar discrepancias entre sus movimientos, lo que llamamos divergencia RSI.

A continuación se muestra lo que hace:

1. Recopilación y preparación de datos

El EA recopila valores RSI junto con los datos de precios correspondientes (mínimos, máximos, cierres y hora) de barras recientes. Esto garantiza que el análisis siempre se base en la información más reciente y completa.

2. Identificación de puntos de inflexión

Luego determina los máximos y mínimos locales tanto en el precio como en los datos RSI. Estos puntos de oscilación sirven como marcadores de referencia para nuestro análisis de divergencia.
3. Detección de divergencia regular

  • Divergencia alcista regular: el EA busca instancias en las que el precio forma un mínimo más bajo mientras que el RSI forma un mínimo más alto, lo que indica que una tendencia bajista puede estar perdiendo impulso y podría revertirse al alza.
  • Divergencia bajista regular: también busca situaciones en las que el precio alcanza un máximo más alto mientras que el RSI forma un máximo más bajo, lo que indica que una tendencia alcista podría estar llegando a su fin a medida que el impulso disminuye.

4. Detección de divergencias ocultas
  • Divergencia alcista oculta: En una tendencia alcista, si el precio forma un mínimo más alto pero el RSI registra un mínimo más bajo, el EA identifica esto como una señal de que la tendencia alcista general sigue siendo fuerte a pesar de un retroceso temporal.
  • Divergencia bajista oculta: Por el contrario, durante una tendencia bajista, si el precio alcanza un máximo más bajo mientras que el RSI muestra un máximo más alto, se confirma que es probable que la tendencia bajista continúe.

5. Generación de señales visuales y de registro

Una vez detectada una divergencia, ya sea regular u oculta, el EA marca visualmente el evento en el gráfico (utilizando flechas y etiquetas) y registra los detalles de la señal para su posterior análisis o backtesting. Para obtener más información sobre cómo se llevan a cabo los procesos anteriores, consulte la sección Desglose del código a continuación.


Código MQL5

//+--------------------------------------------------------------------+
//|                                                RSI Divergence.mql5 |
//|                                 Copyright 2025, Christian Benjamin |
//|                                               https://www.mql5.com |
//+--------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

//---- Global variables
int      rsiHandle;         // Handle for the RSI indicator
double   rsiBuffer[];       // Buffer for RSI values
double   lowBuffer[];       // Buffer for low prices
double   highBuffer[];      // Buffer for high prices
double   closeBuffer[];     // Buffer for close prices
datetime timeBuffer[];       // Buffer for bar times
int      g_totalBars = 0;   // Number of bars in our copied arrays
datetime lastBarTime = 0;   // Time of last closed bar

//---- Structure to hold signal information
struct SignalInfo
  {
   string            type;         // e.g. "RegBearish Divergence", "HiddenBullish Divergence"
   int               barIndex;     // Bar index where the signal was generated
   datetime          signalTime;   // Time of the signal bar
   double            signalPrice;  // Price used for the signal (swing high for bearish, swing low for bullish)
  };

SignalInfo signals[];  // Global array to store signals

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
   EvaluateSignalsAndPrint();
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Process once per new closed candle (using bar1's time)
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

//--- Copy RSI data
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
     {
      Print("Error copying RSI data");
      return;
     }

//--- Copy price and time data
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
     {
      Print("Error copying price data");
      return;
     }

   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
     {
      Print("Error copying time data");
      return;
     }

   g_totalBars = InpLookback;

//--- Identify swing lows and swing highs
   int swingLows[];
   int swingHighs[];
   int startIndex = InpSwingLeft;
   int endIndex   = g_totalBars - InpSwingRight;
   for(int i = startIndex; i < endIndex; i++)
     {
      if(IsSignificantSwingLow(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingLows, ArraySize(swingLows) + 1);
         swingLows[ArraySize(swingLows) - 1] = i;
        }
      if(IsSignificantSwingHigh(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingHighs, ArraySize(swingHighs) + 1);
         swingHighs[ArraySize(swingHighs) - 1] = i;
        }
     }

//--- Bearish Divergence (using swing highs)
   if(ArraySize(swingHighs) >= 2)
     {
      ArraySort(swingHighs); // ascending order: index 0 is most recent
      int recent   = swingHighs[0];
      int previous = swingHighs[1];

      // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
      if(highBuffer[recent] > highBuffer[previous] &&
         rsiBuffer[recent] < rsiBuffer[previous] &&
         (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
        {
         Print("Regular Bearish Divergence detected at bar ", recent);
         DisplaySignal("RegBearish Divergence", recent);
        }
      // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
      else
         if(highBuffer[recent] < highBuffer[previous] &&
            rsiBuffer[recent] > rsiBuffer[previous] &&
            (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
           {
            Print("Hidden Bearish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBearish Divergence", recent);
           }
     }

//--- Bullish Divergence (using swing lows)
   if(ArraySize(swingLows) >= 2)
     {
      ArraySort(swingLows); // ascending order: index 0 is most recent
      int recent   = swingLows[0];
      int previous = swingLows[1];

      // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
      if(lowBuffer[recent] < lowBuffer[previous] &&
         rsiBuffer[recent] > rsiBuffer[previous] &&
         (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
        {
         // Optionally require the earlier swing's RSI be oversold
         if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
           {
            Print("Regular Bullish Divergence detected at bar ", recent);
            DisplaySignal("RegBullish Divergence", recent);
           }
        }
      // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
      else
         if(lowBuffer[recent] > lowBuffer[previous] &&
            rsiBuffer[recent] < rsiBuffer[previous] &&
            (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
           {
            Print("Hidden Bullish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBullish Divergence", recent);
           }
     }
  }

//+------------------------------------------------------------------------+
//| IsSignificantSwingLow: Determines if the bar at 'index' is a swing low |
//+------------------------------------------------------------------------+
bool IsSignificantSwingLow(int index, int left, int right)
  {
   double currentLow = lowBuffer[index];
// Check left side for a local minimum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local minimum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+--------------------------------------------------------------------------+
//| IsSignificantSwingHigh: Determines if the bar at 'index' is a swing high |
//+--------------------------------------------------------------------------+
bool IsSignificantSwingHigh(int index, int left, int right)
  {
   double currentHigh = highBuffer[index];
// Check left side for a local maximum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local maximum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| DisplaySignal: Draws an arrow on the chart and records the signal|
//+------------------------------------------------------------------+
void DisplaySignal(string signalText, int barIndex)
  {
// Prevent duplicate signals on the same bar (or too close)
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
     }

// Update a "LatestSignal" label for regular signals.
   if(StringFind(signalText, "Reg") != -1)
     {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
        {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
           {
            Print("Failed to create LatestSignal label");
            return;
           }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
        }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
     }

// Create an arrow object for the signal.
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
     {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

      if(StringFind(signalText, "Bullish") != -1)
        {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
        }
      else
         if(StringFind(signalText, "Bearish") != -1)
           {
            arrowCode  = 234; // Wingdings down arrow
            arrowColor = clrRed;
            arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
           }

      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
        {
         Print("Failed to create arrow object ", arrowName);
         return;
        }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
     }

// Record the signal for evaluation.
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;

   UpdateSignalCountLabel();
  }

//+------------------------------------------------------------------+
//| UpdateSignalCountLabel: Updates a label showing signal counts    |
//+------------------------------------------------------------------+
void UpdateSignalCountLabel()
  {
   int regCount = 0, hidCount = 0;
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, "Reg") != -1)
         regCount++;
      else
         if(StringFind(signals[i].type, "Hidden") != -1)
            hidCount++;
     }
   string countText = "Regular Signals: " + IntegerToString(regCount) +
                      "\nHidden Signals: " + IntegerToString(hidCount);
   string countLabel = "SignalCount";
   if(ObjectFind(0, countLabel) == -1)
     {
      if(!ObjectCreate(0, countLabel, OBJ_LABEL, 0, 0, 0))
        {
         Print("Failed to create SignalCount label");
         return;
        }
      ObjectSetInteger(0, countLabel, OBJPROP_CORNER, 0);
      ObjectSetInteger(0, countLabel, OBJPROP_XDISTANCE, 10);
      ObjectSetInteger(0, countLabel, OBJPROP_YDISTANCE, 40);
      ObjectSetInteger(0, countLabel, OBJPROP_COLOR, clrYellow);
     }
   ObjectSetString(0, countLabel, OBJPROP_TEXT, countText);
  }

//+--------------------------------------------------------------------+
//| EvaluateSignalsAndPrint: After backtesting, prints signal accuracy |
//+--------------------------------------------------------------------+
void EvaluateSignalsAndPrint()
  {
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
     {
      Print("Error copying complete close data for evaluation");
      return;
     }
   ArraySetAsSeries(closeAll, true);

   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;

   for(int i = 0; i < ArraySize(signals); i++)
     {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];

      if(StringFind(signals[i].type, "Bullish") != -1)
        {
         if(StringFind(signals[i].type, "Reg") != -1)
           {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
           }
         else
            if(StringFind(signals[i].type, "Hidden") != -1)
              {
               hidTotal++;
               hidEval++;
               if(evalClose > signals[i].signalPrice)
                  hidCorrect++;
              }
         totalEvaluated++;
        }
      else
         if(StringFind(signals[i].type, "Bearish") != -1)
           {
            if(StringFind(signals[i].type, "Reg") != -1)
              {
               regTotal++;
               regEval++;
               if(evalClose < signals[i].signalPrice)
                  regCorrect++;
              }
            else
               if(StringFind(signals[i].type, "Hidden") != -1)
                 {
                  hidTotal++;
                  hidEval++;
                  if(evalClose < signals[i].signalPrice)
                     hidCorrect++;
                 }
            totalEvaluated++;
           }
     }

   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;

   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
  }
//+------------------------------------------------------------------+


Desglose del código

1. Información del encabezado y parámetros de entrada

En la parte superior de nuestro script, tenemos un encabezado bien definido que proporciona información clave sobre el código.

Información sobre el archivo y el autor


El encabezado especifica el nombre del archivo (RSI Divergence.mql5), el aviso de derechos de autor y un enlace al perfil del autor. Esto garantiza la atribución adecuada y proporciona a los usuarios un punto de referencia si necesitan comprobar si hay actualizaciones o documentación adicional.

Directivas de versionado y compilación

Las directivas #property establecen propiedades importantes, como el número de versión y el uso de reglas de compilación estrictas (#property strict). Esto ayuda a mantener la coherencia y a reducir los posibles errores durante el desarrollo y la implementación. Continuando, la sección de parámetros de entrada es esencial para la personalización. Estos parámetros le permiten a usted, o a cualquier usuario, ajustar con precisión el comportamiento de la lógica de detección de divergencias sin modificar el código central. A continuación se presentan algunos aspectos destacados:

Parámetros de detección de RSI y oscilación

  • InpRSIPeriod: Establece el período para el indicador RSI.
  • InpSwingLeft e InpSwingRight: Definen cuántos bares de cada lado se tienen en cuenta al detectar puntos de oscilación. Ajustar estos valores hace que la detección de balanceo sea más flexible o más estricta.

Configuración de divergencia y evaluación de señales

  • InpLookback: Determina cuántos bares en el pasado analizará el script en busca de divergencias.
  • InpEvalBars: Especifica el número de barras que hay que esperar antes de evaluar si una señal ha tenido éxito.
  • InpMinBarsBetweenSignals: Ayuda a evitar señales duplicadas al imponer una separación mínima entre barras para señales similares.

Personalización de la pantalla

  • InpArrowOffset: Establece la distancia (en puntos) a la que las flechas se desplazan del punto de oscilación, lo que mejora la claridad visual del gráfico.

Filtro de umbral RSI opcional

  • InpUseRSIThreshold, junto con InpRSIOversold e InpRSIOverbought, proporciona una capa adicional de filtrado. Esto garantiza que, en caso de divergencia alcista, la oscilación anterior del RSI se encuentre en la zona de sobreventa, si el usuario decide activar este filtro.

//+------------------------------------------------------------------+
//|                                           RSI Divergence.mql5    |
//|                               Copyright 2025, Christian Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

2. Inicialización del indicador

En esta parte, inicializamos nuestro indicador RSI. La función OnInit() crea un identificador para el indicador RSI utilizando parámetros como el símbolo, el marco temporal y el período RSI especificado por el usuario. Este paso es crucial porque todas las operaciones posteriores dependen de tener un identificador RSI válido para recuperar los datos del indicador.

  • La función iRSI se invoca con los parámetros necesarios.
  • Se implementa el manejo de errores para detectar cualquier fallo en la creación del identificador.
  • La inicialización garantiza que nuestro indicador esté listo para la adquisición y el análisis de datos.
int OnInit()
{
   // Create the RSI indicator handle with the specified period
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
   {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

3. Adquisición de datos en cada nueva vela

En la función OnTick(), comprobamos si se ha cerrado una nueva vela antes de procesarla. Esto garantiza que nuestro análisis siempre se realice con datos completos. A continuación, copiamos matrices de valores RSI, mínimos, máximos, cierres y datos temporales durante un periodo de revisión configurable. Al configurar las matrices como series, se garantiza que los datos se ordenen con la barra más reciente en el índice 0.

  • El código espera a la siguiente vela cerrada para evitar procesar datos incompletos.
  • Los datos del RSI y del precio se recuperan utilizando funciones como CopyBuffer y CopyLow/High/Close/Time.
  • El uso de ArraySetAsSeries conserva el orden correcto para el análisis de series temporales.

void OnTick()
{
   // Process only once per new closed candle by comparing bar times
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;
   
   // Copy RSI data for a given lookback period
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
   {
      Print("Error copying RSI data");
      return;
   }
   
   // Copy price data (lows, highs, closes) and time data for analysis
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
   {
      Print("Error copying price data");
      return;
   }
   
   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
   {
      Print("Error copying time data");
      return;
   }
   
   g_totalBars = InpLookback;
   
   // (Further processing follows here...)
}

4. Detección de oscilaciones (identificación de mínimos y máximos de oscilación)

Antes de poder detectar divergencias, primero debemos identificar los puntos de inflexión significativos. Se utilizan dos funciones auxiliares, IsSignificantSwingLow e IsSignificantSwingHigh, para identificar mínimos y máximos locales. Para ello, comparan el valor mínimo o máximo de una barra con las barras vecinas dentro de una ventana determinada y comprueban que la diferencia porcentual cumple un umbral establecido.
  • Las funciones comprueban tanto la izquierda como la derecha de la barra actual.
  • Calculan la diferencia porcentual para garantizar que solo se marquen las variaciones significativas.
  • Este filtrado reduce el ruido, garantizando que nuestro análisis de divergencia se centre en movimientos significativos del mercado.
bool IsSignificantSwingLow(int index, int left, int right)
{
   double currentLow = lowBuffer[index];
   // Check left side for local minimum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local minimum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

bool IsSignificantSwingHigh(int index, int left, int right)
{
   double currentHigh = highBuffer[index];
   // Check left side for local maximum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local maximum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

5. Detección de divergencias: Divergencias bajistas y alcistas

Una vez identificados los puntos de oscilación, el algoritmo compara las oscilaciones recientes para detectar divergencias. Para la divergencia bajista, el código analiza dos máximos oscilantes y verifica si el precio está formando un máximo más alto mientras el RSI muestra un máximo más bajo (o viceversa para la divergencia bajista oculta). Para la divergencia alcista, se comparan de manera similar dos mínimos oscilantes. Un umbral RSI opcional puede validar aún más las señales alcistas al garantizar que la lectura RSI anterior esté en territorio de sobreventa.

  • Para el análisis de divergencia se utilizan dos puntos de oscilación recientes (máximos o mínimos).
  • Las condiciones para las divergencias regulares y ocultas están claramente separadas.
  • Los parámetros opcionales (como la condición de sobreventa del RSI) proporcionan un filtrado adicional para la intensidad de la señal.

// --- Bearish Divergence (using swing highs)
if(ArraySize(swingHighs) >= 2)
{
   ArraySort(swingHighs); // Ensure ascending order: index 0 is most recent
   int recent   = swingHighs[0];
   int previous = swingHighs[1];
   
   // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
   if(highBuffer[recent] > highBuffer[previous] &&
      rsiBuffer[recent] < rsiBuffer[previous] &&
      (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Regular Bearish Divergence detected at bar ", recent);
      DisplaySignal("RegBearish Divergence", recent);
   }
   // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
   else if(highBuffer[recent] < highBuffer[previous] &&
           rsiBuffer[recent] > rsiBuffer[previous] &&
           (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      Print("Hidden Bearish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBearish Divergence", recent);
   }
}

// --- Bullish Divergence (using swing lows)
if(ArraySize(swingLows) >= 2)
{
   ArraySort(swingLows); // Ensure ascending order: index 0 is most recent
   int recent   = swingLows[0];
   int previous = swingLows[1];
   
   // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
   if(lowBuffer[recent] < lowBuffer[previous] &&
      rsiBuffer[recent] > rsiBuffer[previous] &&
      (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      // Optionally require the earlier RSI swing to be oversold
      if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
      {
         Print("Regular Bullish Divergence detected at bar ", recent);
         DisplaySignal("RegBullish Divergence", recent);
      }
   }
   // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
   else if(lowBuffer[recent] > lowBuffer[previous] &&
           rsiBuffer[recent] < rsiBuffer[previous] &&
           (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Hidden Bullish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBullish Divergence", recent);
   }
}

6. Visualización y grabación de señales

Cuando se detecta una divergencia, es importante marcar la señal visualmente y registrar sus detalles para una evaluación posterior. La función DisplaySignal() no solo crea una flecha en el gráfico (utilizando diferentes códigos y colores de flecha para las señales alcistas y bajistas), sino que también actualiza una etiqueta para la última señal y almacena los metadatos de la señal en una matriz global. Este registro sistemático permite realizar posteriormente un backtesting de la estrategia.

  • Las señales duplicadas se evitan comprobando si ya existe una señal para una barra similar.
  • Las señales visuales como flechas y etiquetas mejoran la legibilidad del gráfico.
  • Cada señal se almacena con detalles como tipo, índice de barras, hora y precio, lo que facilita la evaluación posterior del rendimiento.

void DisplaySignal(string signalText, int barIndex)
{
   // Prevent duplicate signals on the same or nearby bars
   for(int i = 0; i < ArraySize(signals); i++)
   {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
   }
     
   // Update a label for the latest regular signal
   if(StringFind(signalText, "Reg") != -1)
   {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
      {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
         {
            Print("Failed to create LatestSignal label");
            return;
         }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
      }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
   }
     
   // Create an arrow object to mark the signal on the chart
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
   {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
          
      if(StringFind(signalText, "Bullish") != -1)
      {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
      }
      else if(StringFind(signalText, "Bearish") != -1)
      {
         arrowCode  = 234; // Wingdings down arrow
         arrowColor = clrRed;
         arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
      }
          
      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
      {
         Print("Failed to create arrow object ", arrowName);
         return;
      }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
   }
     
   // Record the signal details for later backtesting evaluation
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;
     
   UpdateSignalCountLabel();
}

Evaluación de la prueba retrospectiva sobre la desinicialización

Por último, la función EvaluateSignalsAndPrint() se invoca cuando el experto se desinicializa. Esta función evalúa retrospectivamente todas las señales registradas comparando el movimiento del precio unas pocas barras después de la señal contra el precio registrado de la señal. Calcula la precisión de las señales regulares y ocultas, proporcionando información valiosa sobre el rendimiento de nuestra estrategia de divergencia.

  • La función recupera datos históricos de cierre completos.
  • Cada señal se evalúa tras un número fijo de barras (según lo establecido por InpEvalBars).
  • Se calculan métricas de precisión para las señales generales, así como por separado para las señales regulares y ocultas, lo que ayuda en la validación del rendimiento.

void EvaluateSignalsAndPrint()
{
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
   {
      Print("Error copying complete close data for evaluation");
      return;
   }
   ArraySetAsSeries(closeAll, true);
   
   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;
   
   for(int i = 0; i < ArraySize(signals); i++)
   {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];
      
      if(StringFind(signals[i].type, "Bullish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose > signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
      else if(StringFind(signals[i].type, "Bearish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose < signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose < signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
   }
     
   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;
     
   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
}


Pruebas y resultados

Después de compilar correctamente su EA con MetaEditor, arrastre su EA al gráfico para probarlo. Asegúrate de utilizar una cuenta demo para evitar arriesgar dinero real. También puede agregar el indicador RSI a su gráfico para confirmar fácilmente la señal mientras prueba su EA. Para ello, vaya a la pestaña Indicadores, seleccione el indicador RSI en la carpeta Paneles y configure sus parámetros preferidos, asegurándose de que coincidan con los de su EA. Vea el GIF a continuación, que ilustra cómo agregar la ventana del indicador RSI en un gráfico de MetaTrader 5. También se puede ver la señal confirmada, una divergencia alcista regular en un marco de tiempo de un minuto.

Convergencia alcista

Figura 5. Indicador de configuración y resultado de la prueba 1

A continuación se muestra otra prueba que realizamos en Boom 500, confirmada tanto por la acción del precio como por el indicador RSI, que muestra una señal de venta.

Prueba

Figura 6. Resultado de la prueba 2

Se realizó otra prueba utilizando backtesting en el GIF que aparece a continuación, que muestra varios cambios positivos. Si miras con atención, notarás señales de continuación ocultas y señales regulares. Sin embargo, algunas señales deben ser filtradas debido a la falta de confirmación, a pesar de su impacto positivo.

Prueba retrospectiva

Figura 7. Resultado de la prueba 3


Conclusión

Esta herramienta ha demostrado estar extremadamente alineada con la acción del precio, que es el objetivo principal de nuestra serie para crear tantas herramientas de análisis de la acción del precio como sea posible. Realmente he apreciado la eficacia con la que el indicador RSI interactúa con la acción del precio al extraer señales positivas de las divergencias. Las pruebas que hemos realizado han mostrado resultados prometedores y una tendencia positiva.

Sin embargo, creo que es hora de introducir otra mejora que utilice bibliotecas externas para una identificación de swing precisa y exacta, mejorando así la precisión de la señal. Mi consejo es que pruebes la herramienta a fondo y ajustes sus parámetros para adaptarlos a tu estilo de trading. Recuerde que cada señal generada debe verificarse antes de ingresar, ya que la herramienta está diseñada para ayudarlo a monitorear el mercado y confirmar su estrategia general.

Fecha Nombre de la herramienta  Descripción Versión  Actualizaciones  Notas
01/10/24 Chart Projector Script para superponer la acción del precio del día anterior con efecto fantasma. 1.0 Lanzamiento inicial Primera herramienta en Lynnchris Tool Chest
18/11/24 Analytical Comment Proporciona información del día anterior en formato tabular y anticipa la dirección futura del mercado. 1.0 Lanzamiento inicial Segunda herramienta en Lynnchris Tool Chest
27/11/24 Analytics Master Actualización periódica de las métricas del mercado cada dos horas  1.01 Segundo lanzamiento Tercera herramienta en Lynnchris Tool Chest
02/12/24 Analytics Forecaster  Actualización periódica de las métricas del mercado cada dos horas con integración de Telegram 1.1 Tercera edición Herramienta número 4
09/12/24 Volatility Navigator El EA analiza las condiciones del mercado utilizando los indicadores Bandas de Bollinger, RSI y ATR 1.0 Lanzamiento inicial Herramienta número 5
19/12/24 Mean Reversion Signal Reaper  Analiza el mercado utilizando la estrategia de reversión a la media y proporciona señales.  1.0  Lanzamiento inicial  Herramienta número 6 
09/01/25  Signal Pulse  Analizador de múltiples marcos temporales 1.0  Lanzamiento inicial  Herramienta número 7 
17/01/25  Metrics Board  Panel con botón para análisis  1.0  Lanzamiento inicial Herramienta número 8 
21/01/25 External Flow Análisis a través de bibliotecas externas 1.0  Lanzamiento inicial Herramienta número 9 
27/01/25 VWAP Volume Weighted Average Price   1.3  Lanzamiento inicial  Herramienta número 10 
02/02/25  Heikin Ashi  Identificación de señales de suavizado y reversión de tendencias  1.0  Lanzamiento inicial  Herramienta número 11
04/02/25  FibVWAP  Generación de señales mediante análisis de Python  1.0  Lanzamiento inicial  Herramienta número 12
14/02/25  RSI DIVERGENCE  Acción del precio frente a divergencias del RSI  1.0  Lanzamiento inicial  Herramienta número 13 

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

Archivos adjuntos |
RSI_DIVERGENCE.mq5 (33.61 KB)
linfo2
linfo2 | 18 feb 2025 en 20:05
Gracias Chris una vez más por sus artículos bien pensados y útiles. Muy apreciado como una plantilla para mí modificar. También aprecio su pensamiento erudito y aplicaciones prácticas. Para mí tuve problemas con EvaluateSignalsAndPrint , está escrito que la función devuelve Error Copying Compete close data for evaluation . El último error es '4003' . No soy lo suficientemente inteligente como para averiguar por qué cuando se utiliza la variable WHOLE_ARRAY la función falla . Para mí, si reemplazo el 'WHOLE_ARRAY en el copyclose con un recuento de las barras cerradas puedo obtener un retorno 'Backtest Signal Evaluation' . Por cierto, estoy en una zona horaria extraña GMT +13 y a veces tengo problemas con las fechas y horas locales y del servidor, pero tal vez esto pueda ayudar a alguien.
Gestor de riesgos profesional remoto para Forex en Python Gestor de riesgos profesional remoto para Forex en Python
Hoy crearemos un gestor de riesgos profesional remoto para Forex en Python, y los desplegaremos en un servidor paso a paso. En el transcurso del artículo entenderemos cómo gestionar programáticamente los riesgos en Forex, y cómo no agotar más nuestro depósito en el mundo de las divisas.
Algoritmo del restaurador de éxito —  Successful Restaurateur Algorithm (SRA) Algoritmo del restaurador de éxito — Successful Restaurateur Algorithm (SRA)
El algoritmo del restaurador de éxito (SRA) es un innovador método de optimización inspirado en los principios de la gestión de restaurantes. A diferencia de los enfoques tradicionales, el SRA no descarta las soluciones débiles, sino que las mejora combinándolas con elementos de las que han tenido éxito. El algoritmo muestra resultados competitivos y ofrece una nueva perspectiva sobre el equilibrio entre investigación y explotación en los problemas de optimización.
Cierres parciales condicionales (Parte 1): Creación de la clase base Cierres parciales condicionales (Parte 1): Creación de la clase base
En este artículo implementaremos un nuevo método para la gestión de posiciones, parecido a los cierres parciales "simples" que implementamos anteriormente, pero con una diferencia importante. En lugar de basarse en niveles de takeprofit fijos, este enfoque aplica los cierres parciales al momento de cumplirse cierta condición específica. De ahí su nombre: "Cierres parciales condicionales". En esta primera parte de la implementación en MQL5 veremos cómo funciona esta técnica de gestión de posiciones.
Redes neuronales en el trading: Integración de la teoría del caos en la previsión de series temporales (Final) Redes neuronales en el trading: Integración de la teoría del caos en la previsión de series temporales (Final)
Seguimos integrando en los modelos comerciales los métodos propuestos por los autores del framework Attraos. Recordemos que este framework usa conceptos de la teoría del caos para resolver problemas de previsión de series temporales, interpretándolos como proyecciones de sistemas dinámicos caóticos multidimensionales.