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
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.
- Introducción
- Descripción general del indicador
- Lógica estratégica
- Diseño del asesor experto
- Pruebas
- Conclusión
Descripción general del indicador
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,015Al 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.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18551
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.
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
Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 01): Desarrollo de una biblioteca SQLite3 inspirada en Python
Negociamos con opciones sin opciones (Parte 2): Aplicación en el trading real
Características del Wizard MQL5 que debe conocer (Parte 72): Uso de los patrones del MACD y del OBV con aprendizaje supervisado
- 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