English Русский 中文 Deutsch 日本語
preview
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 30): Commodity Channel Index (CCI), Zero Line EA

Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 30): Commodity Channel Index (CCI), Zero Line EA

MetaTrader 5Sistemas comerciales |
23 0
Christian Benjamin
Christian Benjamin

Introducción

En este artículo, creamos una herramienta basada en MQL5 que simplifica el trading basado en la acción del precio. El sistema automatiza una estrategia basada en cuatro elementos: un CCI dual, una EMA de 34 períodos, el ATR y la acción del precio pura. Tras revisar cada indicador y esbozar la estrategia de negociación completa, explicaremos paso a paso el desarrollo del Asesor Experto, lo probaremos, analizaremos los resultados y sacaremos conclusiones. En la tabla de la última página del artículo se presenta un resumen de las demás herramientas que hemos desarrollado.

Por favor, revise el índice a continuación.


Descripción general del indicador

Commodity Channel Index (CCI)

El Commodity Channel Index (CCI) es un oscilador de impulso que compara el precio actual con su promedio reciente para detectar posibles condiciones de sobrecompra, sobreventa o reversión de tendencia. El Commodity Channel Index (CCI) se puede calcular como se ilustra a continuación:

CCI = (Precio típico − SMA) / (0,015 × Desviación media)

Precio típico (TP) – Se obtiene sumando los precios máximo, mínimo y de cierre del periodo y dividiendo luego ese total entre tres:

           h + l + c  
TP =       ---------
               3

SMA - Media móvil simple
Una media móvil simple (SMA) toma los precios típicos de un intervalo determinado, como las 20 sesiones más recientes, los suma y los divide entre el número de períodos. Este proceso suaviza las fluctuaciones diarias, lo que permite apreciar mejor la tendencia subyacente.

Desviación media
Es la media de las desviaciones absolutas de los precios típicos respecto a su media móvil simple.

Esta estadística expresa la distancia media a la que se sitúa cada precio típico respecto a la media móvil simple (SMA), lo que ofrece un indicador práctico de la variabilidad de los precios (volatilidad) durante el periodo seleccionado.

Factor constante 0,015

Al multiplicar por este coeficiente fijo, se comprime el resultado del CCI de tal forma que la gran mayoría de los valores se sitúan aproximadamente entre −100 y +100, lo que hace que el indicador sea más fácil de interpretar y permite que destaquen las desviaciones bruscas que indican condiciones pronunciadas de sobrecompra o sobreventa.

Desarrollado por Donald Lambert y publicado por primera vez en 1980 para analizar los movimientos cíclicos de las materias primas, este indicador se aplica hoy en día de forma generalizada a las acciones, las divisas y otras clases de activos. En esta estrategia, utilizamos dos indicadores CCI simultáneamente: un CCI rápido (25) para captar el impulso a corto plazo y un CCI lento (50) para evaluar la fortaleza general del mercado. La mayoría de los valores del CCI se sitúan entre –100 y +100, y las desviaciones fuera de ese rango suelen revelar una fortaleza o debilidad excepcionales. Aunque estos niveles externos pueden servir como filtro de volatilidad, la línea cero sigue siendo el principal factor desencadenante de nuestra estrategia. Un cruce de la zona negativa a la positiva indica un posible cambio hacia un impulso alcista; un movimiento en la dirección opuesta señala una presión bajista incipiente.

Dado que los cruces de la línea cero se producen antes que las rupturas de ±100, permiten a nuestro asesor experto realizar entradas más rápidas, aunque con la posibilidad de que se produzcan ocasionalmente señales erráticas. Para atenuar ese ruido, el EA busca, de forma opcional, una confirmación adicional: un movimiento sostenido por encima de +100 refuerza las señales de compra, y una caída por debajo de –100 refuerza las señales de venta. En resumen, los cruces de la línea cero generan las alertas principales, mientras que los niveles de ±100 actúan como un filtro secundario y no como un sustituto.

Figura 2: Indicador CCI

A continuación se explica cómo crear dos manejadores CCI en MQL5, gestionarlos correctamente y recuperar sus valores. 

  • Creación de los manejadores CCI en OnInit() con:

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);

  • Copiar valores de búfer con CopyBuffer() en OnTick():

CopyBuffer(handleCCI_Long,  0, 0, 2, cciL);
CopyBuffer(handleCCI_Short, 0, 0, 1, cciS);

Media móvil exponencial (EMA) de 34 períodos

Una media móvil exponencial (EMA, por sus siglas en inglés) es un indicador de seguimiento de tendencias que responde rápidamente a la nueva información de precios asignando mayor peso a las velas más recientes. Esta ponderación exponencial filtra el ruido a corto plazo y facilita la visualización de la tendencia predominante. En un gráfico de una hora, suele utilizarse una media móvil exponencial (EMA) de 34 períodos, ya que abarca aproximadamente una semana de trading. Opera únicamente en la dirección de la EMA de 34 períodos: abre posiciones largas cuando el precio se sitúe por encima de la EMA, y abre posiciones cortas cuando el precio se sitúe por debajo de ella. Esta regla funciona como un filtro de tendencia continuo, evitando que la estrategia realice operaciones en contra de la tendencia.

Figura 3: EMA de 34 períodos

A continuación se muestra cómo crear un manejador EMA de 34 períodos en MQL5.

  • Declaración:

input int EMAPeriod = 34;

  • Inicialización:

handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

  • Copia del búfer (misma lógica):

double ema34[1];
if(CopyBuffer(handleEMA, 0, 0, 1, ema34) < 1) return;
double ema_now = ema34[0];

Rango verdadero promedio (ATR)

El Average True Range (ATR) es un indicador de volatilidad desarrollado por J. Welles Wilder Jr. y lo presentó en su libro de 1978 «New Concepts in Technical Trading Systems». Por defecto, calcula la media del rango verdadero (True Range) sobre un periodo de cálculo de 14 velas/períodos, por lo que refleja las oscilaciones «en bruto» de los precios del mercado.

En este artículo, utilizamos el Rango Verdadero Promedio (ATR) para determinar y establecer nuestros niveles de stop-loss (SL) y take-profit (TP). A continuación se muestra cómo implementarlo en MQL5:

  • Declaración

int handleATR = INVALID_HANDLE;
input int ATR_Period = 14;

  • Inicialización

handleATR = iATR(_Symbol, _Period, ATR_Period);
if(handleATR == INVALID_HANDLE) return INIT_FAILED;

  • Búfer de copia

double atrBuf[1];
if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) > 0)
{
   double atrValue = atrBuf[0];
   // use atrValue here…
}

  • Limpieza

if(handleATR != INVALID_HANDLE)
    IndicatorRelease(handleATR);


Lógica estratégica

En esta sección, explicamos cómo nuestro Asesor Experto CCI Zero-Line genera señales de entrada combinando dos indicadores Commodity Channel Index (CCI) y una media móvil exponencial (EMA) de 34 periodos. Cuando el CCI cruza por encima de la línea cero, indica que el impulso del mercado está cambiando al alza, lo que sugiere una posible señal alcista o un aumento de la presión compradora. Por el contrario, cuando el CCI cruza por debajo de la línea cero, esto indica que el impulso del mercado está cambiando a la baja, lo que apunta a una posible tendencia bajista o a un aumento de la presión vendedora. 

Cuando el precio se sitúa por encima de la EMA de 34 periodos, generalmente indica un cambio alcista en el impulso del mercado, lo que implica una posible tendencia alcista o una oportunidad de compra. Por otro lado, si el precio cae por debajo de la EMA de 34, sugiere una disminución del impulso y el posible inicio de una tendencia bajista, lo que indica una posible señal de venta.

Indicadores utilizados

  • CCI (25) – Índice rápido del canal de materias primas
  • CCI (50) – Índice de canal de materias primas lento
  • EMA (34) – Media móvil exponencial de 34 períodos sobre los precios de cierre

Criterios de señal de compra

  • CCI (25) por encima de cero: El CCI (25) debe estar por encima de la línea cero, lo que indica un impulso alcista a corto plazo.
  • CCI (50) Cruce de la línea cero: El CCI (50) debe cruzar al alza la línea cero, lo que confirma un cambio de tendencia a largo plazo.
  • Confirmación del precio: La vela actual debe cerrar por encima de la EMA (34), lo que valida la tendencia alcista.

Figura 4: Condiciones de la señal de compra

Cuando se dan las tres condiciones en una nueva barra, el EA registra una señal de compra.

Criterios de señal de venta

  • CCI (25) por debajo de cero: El CCI (25) debe estar por debajo de la línea cero, lo que indica un impulso bajista a corto plazo.
  • CCI (50) Cruce de la línea cero: El CCI (50) debe cruzar a la baja la línea cero, lo que confirma un cambio bajista a largo plazo.
  • Confirmación del precio: La vela actual debe cerrar por debajo de la EMA (34), lo que valida la tendencia bajista.

Figura 5: Condiciones de la señal de venta

Cuando se dan las tres condiciones en una nueva barra, el EA registra una señal de venta.


Diseño del asesor experto

Cuando se inicia el EA, primero configura los manejadores de indicadores necesarios que le permiten obtener datos en tiempo real del gráfico. Este proceso tiene lugar en la función OnInit(). Los manejadores incluyen dos CCI, uno con un período largo (50) para identificar la tendencia general y otro con un período más corto (25) para servir como indicador de confirmación, y una EMA con un período de 34 que actúa como filtro de tendencia. En concreto, el código crea estos manejadores utilizando funciones como iCCI() e iMA(), que solicitan los indicadores al motor interno de MetaTrader.

La creación correcta de estos identificadores es fundamental; si alguno no se inicializa correctamente, lo que se indica devolviendo INVALID_HANDLE, el EA se detendrá, evitando señales falsas o errores durante el funcionamiento. Además, existe un identificador ATR opcional, que se crea solo si la gestión de stop-loss y take-profit basada en ATR está habilitada (parámetro UseATR).

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(UseATR)
    handleATR = iATR(_Symbol, _Period, ATR_Period);

  • Este enfoque garantiza que el EA (Asesor Experto) disponga de todos los flujos de datos necesarios para tomar decisiones de trading informadas en cada tick.

El Asesor Experto incluye una función para restringir la operativa a horas específicas, en particular a la sesión de Tokio para el par USD/JPY, lo que ayuda a evitar operar durante períodos de bajo volumen que pueden causar movimientos de precios impredecibles. Esto se lleva a cabo comprobando la hora actual del servidor dentro de la función OnTick(). Si el filtro de sesión está activado, el código convierte la hora actual a un formato estructurado (MqlDateTime) y compara el componente de la hora con las horas de inicio y fin definidas por el usuario. Si la hora actual queda fuera de este intervalo, el EA simplemente sale de la función, sin realizar más análisis ni operaciones.

if(UseTokyoSessionFilter && _Symbol == "USDJPY")
{
    datetime now = TimeTradeServer();
    MqlDateTime tm;
    TimeToStruct(now, tm);
    int hr = tm.hour;
    if(hr < TokyoStartHour || hr >= TokyoEndHour)
        return;
}

  • Operar únicamente durante las horas designadas ayuda a reducir el riesgo de deslizamiento y de señales falsas, que son comunes en mercados con poca actividad durante las horas de menor actividad.

Para evitar evaluaciones múltiples dentro de la misma vela, el EA emplea una lógica simple pero efectiva: registra la marca de tiempo de la última barra procesada y solo continúa si se ha formado una nueva barra. Esto se logra mediante una variable estática llamada lastTime, que conserva su valor entre las llamadas a funciones. Cuando llega un nuevo tick, el código compara el tiempo actual de la barra (iTime()) con este valor almacenado. Si coinciden, la función regresa anticipadamente, lo que garantiza que todos los cálculos y decisiones se realicen una sola vez por barra.

static datetime lastTime=0;
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastTime) return; // same bar, skip
lastTime = t;

  • Al optimizar el rendimiento, evitar señales duplicadas y alinear la ejecución con el análisis típico del operador basado en marcos temporales, este enfoque mejora la eficiencia y la precisión generales de la estrategia de negociación.

En cada nueva barra, el EA llama a CopyBuffer() para obtener los últimos valores de CCI (25), CCI (50) y EMA (34) (incluida la barra anterior para la detección de cruces). El CCI lento (50) define la tendencia general, mientras que el CCI rápido (25) confirma el impulso de entrada. La EMA filtra el ruido para asegurar que el precio se mueva en consonancia con la tendencia. Si ha habilitado las órdenes de stop basadas en ATR, el EA también recupera el ATR actual (14).

double cciL[2], cciS[1], emaVal[1];
if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return;
if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return;
if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return;

double atrValue = 0.0;
if(UseATR)
{
    double atrBuf[1];
    if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return;
    atrValue = atrBuf[0];
}

  • En conjunto, estas lecturas impulsan todas las decisiones de trading: los CCI detectan reversiones o continuaciones, la EMA valida la alineación de la tendencia y el ATR dimensiona dinámicamente su riesgo.

La lógica central de las operaciones se basa en detectar cuándo el índice CCI a largo plazo cruza su línea cero, lo que indica un posible cambio de tendencia. Un cruce ascendente (de negativo a positivo) sugiere un movimiento alcista, mientras que un cruce descendente indica una tendencia bajista. El código captura estos momentos comparando los valores anteriores y actuales del búfer CCI largo. Una vez detectado un cruce, el EA aplica filtros adicionales: el CCI corto debe coincidir (positivo para alcista, negativo para bajista) y el precio actual debe estar por encima o por debajo de la EMA, respectivamente.

double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

if(crossUp && cciS[0] > 0 && price > emaVal[0])
    RegisterSignal(true, price, atrValue);
if(crossDown && cciS[0] < 0 && price < emaVal[0])
    RegisterSignal(false, price, atrValue);

  • Esta confirmación por capas ayuda a filtrar las señales falsas, asegurando que las operaciones se realicen solo cuando varios indicadores coinciden y el filtro de tendencia respalda el movimiento.

Una vez identificada una señal válida, el EA calcula los niveles apropiados para el stop-loss (SL) y el take-profit (TP). Si la gestión basada en ATR está habilitada, estos niveles se derivan dinámicamente multiplicando el valor ATR actual por el multiplicador definido por el usuario (por ejemplo, 1,5). El stop loss se coloca a una distancia del precio de entrada, ya sea por debajo para las operaciones de compra o por encima para las operaciones de venta. El TP se establece proporcionalmente más lejos en función de la relación riesgo-recompensa, normalmente 1,5 veces la distancia SL.

double dist = UseATR ? atrValue * ATR_Multiplier : SLBufferPoints * _Point;
double slPrice = isBuy ? price - dist : price + dist;
double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio;

  • A continuación, el Asesor Experto crea indicadores visuales en el gráfico: flechas justo fuera de la vela actual para marcar los puntos de entrada y líneas horizontales que muestran los niveles de Stop Loss (SL) y Take Profit (TP).

ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor);
ObjectCreate(0, "SL_Line", OBJ_HLINE, 0, 0, slPrice);
ObjectCreate(0, "TP_Line", OBJ_HLINE, 0, 0, tpPrice);

  • Estas señales visuales ayudan a verificar las señales visualmente y a garantizar que la lógica del EA se ajuste a las condiciones reales del mercado.

Tras trazar los marcadores visuales, el EA emite un mensaje de alerta con información detallada sobre la operación: precio de entrada, niveles de SL y TP.

Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") +
      StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice));

También actualiza matrices de seguimiento internas que almacenan los niveles de SL y TP de cada operación, junto con un indicador booleano que señala si el resultado de la operación se ha resuelto. Estos conjuntos de datos son esenciales para supervisar las operaciones en curso, especialmente durante las pruebas retrospectivas o las operaciones en tiempo real, ya que permiten al EA gestionar múltiples posiciones abiertas simultáneamente y evaluar su rendimiento.

signalSL[totalSignals] = slPrice;
signalTP[totalSignals] = tpPrice;
resolved[totalSignals] = false;

La función ResolveSignals() se encarga de realizar el seguimiento de las operaciones activas. Recorre todas las señales abiertas, comprobando los precios máximos y mínimos de las barras recientes para ver si se han alcanzado los niveles de SL o TP. Si el máximo supera el TP, la operación se considera ganadora; si el mínimo rompe el SL, se considera perdedora. Este proceso actualiza los contadores de señales y victorias totales, proporcionando información estadística sobre la efectividad de la estrategia. Este monitoreo es crucial para las pruebas retrospectivas, donde el registro preciso de los resultados influye en las métricas de rendimiento generales, como la tasa de éxito, el factor de beneficio y la reducción máxima.

double high = iHigh(_Symbol, _Period, idx);
double low = iLow(_Symbol, _Period, idx);
if(high >= signalTP[i]) { winSignals++; resolved[i]=true; }
if(low <= signalSL[i]) { resolved[i]=true; }

Cuando se retira el EA o se cierra el gráfico, la función OnDeinit() garantiza que todos los manejadores de indicadores se liberen mediante IndicatorRelease(). También elimina cualquier objeto gráfico creado durante la operación, como flechas y líneas horizontales. Una gestión adecuada de los recursos evita las fugas de memoria y mantiene el gráfico despejado, lo que evita residuos gráficos y permite que futuras ejecuciones u otros EA funcionen sin problemas.

IndicatorRelease(handleCCI_Long);
IndicatorRelease(handleCCI_Short);
ObjectDelete(0, "SL_Line");
ObjectDelete(0, "TP_Line");

Código completo

//+------------------------------------------------------------------+
//|                                                  CCI Zero-Line EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade\Trade.mqh>
#include <Tools\Datetime.mqh>

CTrade trade;

//--- Session filter for USDJPY (Tokyo session)
input bool UseTokyoSessionFilter = false;  // enable Tokyo session filter
input int  TokyoStartHour        = 0;      // session start hour (server time)
input int  TokyoEndHour          = 9;      // session end hour   (server time, exclusive)

//--- SL/TP settings
input bool   UseATR           = true;   // true = ATR-based SL/TP, false = fixed points
input int    ATR_Period       = 14;     // ATR look-back
input double ATR_Multiplier   = 1.5;    // SL/TP distance = ATR × this
input double SLBufferPoints   = 10.0;   // fallback SL offset in pips if UseATR=false
input double RiskRewardRatio  = 1.5;    // TP = SL × 1.5 (1:1.5 RR)

//--- Indicator periods
input int CCI_LongPeriod   = 50;  // CCI long period (zero-line cross)
input int CCI_ShortPeriod  = 25;  // CCI short period (confirmation)
input int EMAPeriod        = 34;  // EMA period for trend filter

//--- Arrow colors
input color BuyArrowColor  = clrLime;
input color SellArrowColor = clrRed;

//--- Indicator handles
int handleCCI_Long = INVALID_HANDLE;
int handleCCI_Short= INVALID_HANDLE;
int handleEMA      = INVALID_HANDLE;
int handleATR      = INVALID_HANDLE;

//--- Signal-tracking arrays
int    totalSignals = 0;
int    winSignals   = 0;
int    signalBar[];
double signalSL[];
double signalTP[];
bool   resolved[];

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
   handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
   handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   if(handleCCI_Long == INVALID_HANDLE ||
      handleCCI_Short== INVALID_HANDLE ||
      handleEMA      == INVALID_HANDLE)
      return(INIT_FAILED);

   if(UseATR)
     {
      handleATR = iATR(_Symbol, _Period, ATR_Period);
      if(handleATR == INVALID_HANDLE)
         return(INIT_FAILED);
     }

   if(RiskRewardRatio <= 0 ||
      ATR_Multiplier   <= 0 ||
      (SLBufferPoints <= 0 && !UseATR))
      return(INIT_FAILED);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(handleCCI_Long  != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Long);
   if(handleCCI_Short != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Short);
   if(handleEMA       != INVALID_HANDLE)
      IndicatorRelease(handleEMA);
   if(UseATR && handleATR != INVALID_HANDLE)
      IndicatorRelease(handleATR);

   ObjectDelete(0, "SL_Line");
   ObjectDelete(0, "TP_Line");
  }

//+------------------------------------------------------------------+
//| Strategy Tester summary                                          |
//+------------------------------------------------------------------+
double OnTester()
  {
   double winRate = totalSignals > 0
                    ? 100.0 * winSignals / totalSignals
                    : 0.0;
   PrintFormat("=== Backtest Win-Rate ===\nSignals: %d  Wins: %d  Win-Rate: %.2f%%",
               totalSignals, winSignals, winRate);
   return(winRate);
  }

//+------------------------------------------------------------------+
//| Tick handler                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
// Tokyo session filter for USDJPY
   if(UseTokyoSessionFilter && _Symbol == "USDJPY")
     {
      datetime now = TimeTradeServer();
      MqlDateTime tm;
      TimeToStruct(now, tm);
      int hr = tm.hour;
      if(hr < TokyoStartHour || hr >= TokyoEndHour)
         return;
     }

// Only act on new bar
   static datetime lastTime = 0;
   datetime t = iTime(_Symbol, _Period, 0);
   if(t == lastTime)
      return;
   lastTime = t;

// Copy indicator buffers
   double cciL[2], cciS[1], emaVal[1];
   if(CopyBuffer(handleCCI_Long,  0, 0, 2, cciL)  < 2)
      return;
   if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS)  < 1)
      return;
   if(CopyBuffer(handleEMA,       0, 0, 1, emaVal) < 1)
      return;

// ATR if needed
   double atrValue = 0.0;
   if(UseATR)
     {
      double atrBuf[1];
      if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1)
         return;
      atrValue = atrBuf[0];
     }

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

// Detect zero-line cross
   bool crossUp   = (cciL[1] < 0 && cciL[0] > 0);
   bool crossDown = (cciL[1] > 0 && cciL[0] < 0);

// Confirm with short CCI & EMA trend filter
   if(crossUp   && cciS[0] >  0 && price > emaVal[0])
      RegisterSignal(true,  price, atrValue);
   if(crossDown && cciS[0] <  0 && price < emaVal[0])
      RegisterSignal(false, price, atrValue);

// Resolve pending signals for SL/TP hits
   ResolveSignals();
  }

//+------------------------------------------------------------------+
//| Register new buy/sell signal                                     |
//+------------------------------------------------------------------+
void RegisterSignal(bool isBuy, double price, double atrVal)
  {
   double dist    = UseATR
                    ? atrVal * ATR_Multiplier
                    : SLBufferPoints * _Point;
   double slPrice = isBuy ? price - dist : price + dist;
   double tpPrice = isBuy
                    ? price + dist * RiskRewardRatio
                    : price - dist * RiskRewardRatio;

// Arrow placement just outside the candle
   datetime barTime = iTime(_Symbol, _Period, 0);
   double   barHigh = iHigh(_Symbol, _Period, 0);
   double   barLow  = iLow(_Symbol, _Period, 0);
   double   offset  = 5 * _Point;  // e.g. 5-pip offset

   double arrowPrice = isBuy
                       ? barLow  - offset
                       : barHigh + offset;

// Use numeric arrow codes directly
   int arrowCode = isBuy ? 233 : 234;

   string name = (isBuy ? "BUY_" : "SELL_")
                 + TimeToString(TimeTradeServer(), TIME_SECONDS);
   ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
   ObjectSetInteger(0, name, OBJPROP_COLOR,     isBuy ? BuyArrowColor : SellArrowColor);
   ObjectSetInteger(0, name, OBJPROP_WIDTH,     2);

// Draw SL/TP lines
   DrawLine("SL_Line", slPrice, clrRed);
   DrawLine("TP_Line", tpPrice, clrLime);

// Alert
   Alert("CCI ZeroLine EMA + R:R 1:1.5 "
         + (isBuy ? "BUY" : "SELL")
         + StringFormat(" @%.5f | SL: %.5f | TP: %.5f",
                        price, slPrice, tpPrice));

// Track for backtest metrics
   totalSignals++;
   ArrayResize(signalBar,  totalSignals);
   ArrayResize(signalSL,   totalSignals);
   ArrayResize(signalTP,   totalSignals);
   ArrayResize(resolved,   totalSignals);

   signalBar[totalSignals-1] = 0;
   signalSL[totalSignals-1]  = slPrice;
   signalTP[totalSignals-1]  = tpPrice;
   resolved[totalSignals-1]  = false;
  }

//+------------------------------------------------------------------+
//| Resolve pending signals (SL/TP checks)                           |
//+------------------------------------------------------------------+
void ResolveSignals()
  {
   int bars = Bars(_Symbol, _Period);
   for(int i = 0; i < totalSignals; i++)
     {
      if(resolved[i])
         continue;
      signalBar[i]++;
      int idx = signalBar[i];
      if(idx >= bars)
         continue;

      double high = iHigh(_Symbol, _Period, idx);
      double low  = iLow(_Symbol, _Period, idx);

      if(high >= signalTP[i])
        {
         winSignals++;
         resolved[i] = true;
        }
      else
         if(low <= signalSL[i])
           {
            resolved[i] = true;
           }
     }
  }

//+------------------------------------------------------------------+
//| Draw a horizontal SL/TP line                                     |
//+------------------------------------------------------------------+
void DrawLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
   ObjectSetString(0, name, OBJPROP_TEXT, name);
  }
//+------------------------------------------------------------------+


Pruebas

A continuación se muestran los resultados de las pruebas retrospectivas de nuestro EA.

He realizado un backtest del EA en USDJPY, y a continuación se muestran los registros del diario.

  • Parámetros
2025.06.30 09:34:54.140   UseTokyoSessionFilter=false
2025.06.30 09:34:54.140   TokyoStartHour=0
2025.06.30 09:34:54.140   TokyoEndHour=9
2025.06.30 09:34:54.140   UseATR=true
2025.06.30 09:34:54.140   ATR_Period=14
2025.06.30 09:34:54.140   ATR_Multiplier=1.5
2025.06.30 09:34:54.140   SLBufferPoints=10.0
2025.06.30 09:34:54.140   RiskRewardRatio=1.0
2025.06.30 09:34:54.140   CCI_LongPeriod=50
2025.06.30 09:34:54.140   CCI_ShortPeriod=25
2025.06.30 09:34:54.140   EMAPeriod=34
2025.06.30 09:34:54.140   BuyArrowColor=65280
2025.06.30 09:34:54.140   SellArrowColor=255

La configuración indica que el filtro de sesión de Tokio está desactivado, lo que permite que las operaciones se realicen fuera del horario específico de Tokio; sin embargo, si se activa, las operaciones estarían restringidas entre las 00:00 y las 09:00, hora de Tokio. El Asesor Experto utiliza el indicador ATR con un período de 14, aplicando un multiplicador de 1,5 para ajustar los niveles de stop-loss y take-profit en función de la volatilidad actual del mercado. Se añade un margen de seguridad de 10 puntos al stop-loss para mitigar las salidas prematuras, mientras que la relación riesgo-recompensa se establece en 1:1, equilibrando las ganancias y pérdidas potenciales.

El CCI utiliza un período de 50 para el CCI lento y de 25 para el CCI rápido, y la estrategia incluye una media móvil exponencial (EMA) con un período de 34. Las señales visuales se representan mediante flechas: el color verde indica señales de compra y el rojo, señales de venta, de acuerdo con sus respectivos códigos de color.

Los parámetros de entrada se pueden personalizar según sus preferencias. Los ajustes proporcionados son los que utilicé durante las pruebas, los cuales arrojaron resultados que considero favorables.

  • Señales

2025.06.30 09:34:54.447 2025.01.02 15:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @157.01800 | SL: 157.45075 | TP: 156.58525
2025.06.30 09:34:56.655 2025.01.09 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @158.04800 | SL: 158.30343 | TP: 157.79257
2025.06.30 09:40:35.572 2025.01.29 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @155.21100 | SL: 155.47704 | TP: 154.94496
2025.06.30 09:40:36.759 2025.01.31 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @154.68200 | SL: 154.25439 | TP: 155.10961
2025.06.30 09:40:45.870 2025.02.26 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.42600 | SL: 149.05400 | TP: 149.79800
2025.06.30 09:40:46.737 2025.02.28 03:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.51900 | SL: 150.03650 | TP: 149.00150
2025.06.30 09:40:49.726 2025.03.06 01:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.14200 | SL: 149.68200 | TP: 148.60200
2025.06.30 09:40:58.522 2025.03.21 16:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @148.97200 | SL: 149.35418 | TP: 148.58982
2025.06.30 09:41:01.363 2025.04.01 00:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.93500 | SL: 149.57425 | TP: 150.29575
2025.06.30 09:41:16.557 2025.04.28 13:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @143.33500 | SL: 143.69232 | TP: 142.97768
2025.06.30 09:41:36.021 2025.06.18 14:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @144.85500 | SL: 145.08911 | TP: 144.62089
2025.06.30 09:41:37.454 2025.06.23 19:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @146.15000 | SL: 146.65711 | TP: 145.64289
2025.06.30 09:41:38.360 2025.06.25 23:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @145.21700 | SL: 145.47286 | TP: 144.96114

  • Tasa de victorias

2025.06.30 09:41:39.206 2025.06.29 23:59:59   Signals: 13  Wins: 10  Win-Rate: 76.92%

A continuación se muestra un GIF que visualiza los resultados de la prueba: las flechas rojas hacia abajo indican señales de venta, las flechas verdes hacia arriba representan señales de compra, las líneas rojas indican niveles de stop-loss y las líneas verdes indican objetivos de take-profit.


Conclusión

En resumen, este EA CCI Zero-Line pone todo lo que necesitas para entradas claras y basadas en reglas directamente en tu gráfico:

  • Utilizamos el CCI (50) para detectar la tendencia general y el CCI (25) para confirmar que el impulso está listo, por lo que solo actuamos cuando ambos coinciden en la dirección de la señal.
  • La EMA de 34 periodos nos impide perseguir picos aleatorios. Si el precio cierra en el lado correcto de esa línea, sabemos que la tendencia es auténtica.
  • Con los stops basados en el ATR, la distancia de nuestro stop-loss se adapta a la volatilidad: es más estrecha en mercados tranquilos y más amplia cuando la situación se agita. Nuestro objetivo de beneficios se mantiene proporcional, lo que garantiza una relación de recompensa constante.

Esa combinación significa que las flechas solo aparecen cuando el precio, el impulso y la volatilidad apuntan en la misma dirección. Se reducen las señales falsas, se logra una gestión de riesgos eficaz y se obtiene total transparencia sobre los motivos por los que se eligió cada nivel de entrada y salida. Ajusta con precisión los periodos del CCI, la longitud de la EMA o el multiplicador del ATR para adaptarlos a cualquier símbolo o marco temporal, y opera con la confianza que te brinda una herramienta basada en un análisis sólido y multifacético. Este Asesor Experto (EA) tiene fines exclusivamente educativos. No lo implemente en el mercado real sin antes realizar pruebas retrospectivas exhaustivas con datos históricos o pruebas en cuentas demo. Asegúrese de estar satisfecho con los resultados antes de considerar cualquier operación en tiempo real.





   
Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Quarters Drawer Script
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter 
PAQ Analysis Tool 
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zero-Line EA

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

Archivos adjuntos |
CCI.mq5 (18.28 KB)
Cómo creamos la plataforma de trading más potente con aprendizaje automático: crónica del desarrollo de MQL y MetaTrader según los archivos, foros y lanzamientos Cómo creamos la plataforma de trading más potente con aprendizaje automático: crónica del desarrollo de MQL y MetaTrader según los archivos, foros y lanzamientos
Crónica técnica del desarrollo de MQL: desde el MQL/MQL II, con sus limitaciones, pasando por el MQL4 procedimental, hasta el MQL5 orientado a objetos, con compilación nativa, API muy completas y un entorno de ingeniería integral. Se presentan las capacidades clave del lenguaje y las integraciones con Python, OpenCL, ONNX, OpenBLAS, bases de datos, DirectX, el AI Assistant basado en agentes y el Model Context Protocol (MCP), que conecta la IA con el terminal de trading, MetaEditor, los datos de mercado, el trading y las herramientas de desarrollo. Se analizan fuentes de archivo sobre los orígenes y la evolución de MetaQuotes/MetaTrader, el lanzamiento de MQL4.COM y MQL5.COM, los campeonatos, Algo Forge y su impacto en el ecosistema.
Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 01): Desarrollo de una biblioteca SQLite3 inspirada en Python Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 01): Desarrollo de una biblioteca SQLite3 inspirada en Python
El módulo sqlite3 de Python ofrece una forma sencilla de trabajar con bases de datos SQLite; es rápido y práctico. En este artículo, vamos a crear un módulo similar a partir de las funciones integradas de MQL5 para trabajar con bases de datos, con el fin de facilitar el trabajo con bases de datos SQLite3 en MQL5, al igual que en Python.
Negociamos con opciones sin opciones (Parte 2): Aplicación en el trading real Negociamos con opciones sin opciones (Parte 2): Aplicación en el trading real
En este artículo se analizan estrategias de opciones sencillas y su implementación en MQL5. Vamos a escribir un asesor experto básico que iremos modernizando y haciendo más complejo.
Características del Wizard MQL5 que debe conocer (Parte 72): Uso de los patrones del MACD y del OBV con aprendizaje supervisado Características del Wizard MQL5 que debe conocer (Parte 72): Uso de los patrones del MACD y del OBV con aprendizaje supervisado
Damos continuidad a nuestro último artículo, en el que presentamos el par de indicadores formado por el MACD y el OBV, analizando cómo se podría mejorar esta combinación mediante el aprendizaje automático. MACD y OBV forman un emparejamiento complementario en cuanto a tendencia y volumen. Nuestro enfoque de aprendizaje automático utiliza una red neuronal convolucional que emplea un kernel para dimensionar sus núcleos y canales, al tiempo que ajusta las previsiones de este par de indicadores. Como siempre, esto se realiza en un archivo de clase de señal personalizado que funciona con el asistente MQL5 para ensamblar un Asesor Experto.