Explorador de la biblioteca estándar de MQL5 (Parte 1): Introducción con CTrade, CiMA y CiATR
Estructura del artículo:
Introducción
Muchos desarrolladores, sobre todo los que se inician en MQL5, suelen evitar el uso de clases y bibliotecas. Esta reticencia es comprensible: la programación procedimental sencilla resulta más directa y fácil de conceptualizar. Aunque este es un punto de partida válido, avanzar hacia sistemas más sofisticados requiere un cambio de mentalidad. Adoptar la programación orientada a objetos (POO) no solo supone una mayor complejidad, sino que, a la larga, permite alcanzar un mayor nivel de profesionalidad, eficiencia y simplicidad.
La biblioteca estándar de MQL5 es la encarnación perfecta de este principio. No se trata de un conjunto de reglas crípticas que haya que dominar, sino más bien de una colección de potentes herramientas ya preparadas, diseñadas para eliminar las tareas repetitivas. No lo vea como reinventar la rueda desde cero, sino como montar un vehículo de alto rendimiento a partir de piezas fabricadas con maestría. La biblioteca le ofrece las piezas del rompecabezas; su tarea consiste en unirlas para crear una imagen completa.
Esta serie de artículos está pensada para servir de guía. Tanto si es un principiante con ganas de crear su primer asesor experto como si es un profesional experimentado que busca simplificar su código, le ayudaremos a comprender la biblioteca estándar. Nuestro objetivo consiste en mostrarle cómo crear sistemas de trading complejos y robustos con un mínimo de código repetitivo, aprovechando el trabajo ya realizado por los desarrolladores de MetaQuotes.
He observado en repetidas ocasiones que muchos desarrolladores pasan por alto la gran utilidad de la biblioteca estándar y, a menudo, se esfuerzan por implementar funciones que ya están disponibles en ella. Esta biblioteca es una puerta de entrada a la simplicidad, ya que ofrece soluciones elegantes a los retos habituales del trading algorítmico.
Además, el potencial de la biblioteca está, en gran medida, sin explotar. Muchos de sus componentes quedan en segundo plano, como tesoros ocultos que esperan ser descubiertos y puestos en práctica. Con cada actualización de MetaTrader 5, la biblioteca estándar se vuelve más potente y completa. Al aprender a trabajar con ella, no nos limitamos a utilizar una herramienta, sino que aprendemos un lenguaje de eficiencia que nos permite ampliar sus capacidades e inventar nuevas funciones por nosotros mismos.
En este artículo levantamos el telón sobre esas joyas ocultas y las examinamos en detalle. Emprendamos así un recorrido para descubrir todo el potencial de la biblioteca estándar de MQL5, transformando así nuestra forma de programar y operar. En esta primera parte de nuestra serie, desarrollamos un asesor experto completo que muestra cómo se pueden combinar tres clases muy utilizadas de la biblioteca estándar de MQL5 —CTrade, CiMA y CiATR— para crear un sistema de trading práctico. El objetivo aquí consiste en presentar una solución plenamente funcional que los lectores puedan compilar, probar y ajustar sin tener que esperar a futuras entregas.Antecedentes
Aunque la documentación de MQL5 ofrece la definición formal de clases como CTrade, CiMA y CiATR —con información sobre su herencia, los métodos disponibles y breves descripciones—, no llega a mostrar a los desarrolladores cómo combinarlas para crear sistemas de trading prácticos. Para alguien que se inicia en esto, esta carencia puede resultar intimidante; la documentación le explica qué hay disponible, pero no cómo utilizarlo de forma eficaz. Por eso este artículo y muchos otros son fundamentales.
Aquí vamos más allá de los listados de la API y pasamos a la aplicación práctica. Vamos a explicar no solo qué representa cada clase, sino también cómo integrarla en el ciclo de vida del asesor experto, cómo deben interpretarse sus resultados y cómo interactúa con otras clases para formar una estrategia completa. En resumen, esta serie sirve de puente entre la documentación de referencia y la práctica real, ofreciendo a los lectores una guía paso a paso desde las definiciones básicas de las clases hasta un robot de trading operativo.
¿Por qué estas clases?
La biblioteca estándar de MQL5 se incluye con cada instalación de MetaTrader 5, y se puede acceder fácilmente a sus archivos fuente en MetaEditor 5. Por defecto, estos archivos se encuentran en el directorio «Include» de su instalación de MQL5 (por ejemplo, MQL5\Include\Trade\ o MQL5\Include\Indicators\). Cada módulo está organizado en archivos de cabecera (.mqh) que definen clases reutilizables, como CTrade, CiMA y CiATR. En MetaEditor, puede explorar fácilmente estos archivos desde el panel «Navegador», en «Include» → «Biblioteca estándar», o abrirlos directamente a través de «Archivo» → «Abrir». Esta accesibilidad le permite utilizar libremente las clases en sus proyectos añadiendo directivas #include, así como examinar el propio código fuente, conocer la estructura de la biblioteca e incluso ampliarla o personalizarla según sus propias necesidades de desarrollo.

Figura 1. Cómo acceder a la biblioteca estándar de MQL5 en MetaEditor 5
CTrade: el motor de ejecución
CTrade es el envoltorio de la biblioteca estándar que se encarga de enviar solicitudes de operaciones y de gestionar las órdenes de forma básica por usted. En lugar de construir manualmente estructuras de bajo nivel como MqlTradeRequest , CTrade ofrece métodos sencillos (Buy, Sell, PositionClose, etc.) y, a continuación, devuelve resultados estandarizados que puede consultar. En el asesor experto, recurriremos a CTrade para todas las tareas de ejecución: abrir posiciones con el volumen calculado, comprobar el estado devuelto, registrar el motivo del fallo cuando se rechace una solicitud y realizar reintentos seguros o la desactivación de la operativa cuando aparezcan errores persistentes. El uso de CTrade hace que el código de ejecución sea más breve, más fácil de auditar y más coherente entre los distintos brókeres.
#include <Trade\Trade.mqh> // Global declarations CTrade trade;
CiATR: volatilidad y cálculo del tamaño de la posición
CiATR (de Indicators\Oscillators.mqh) indica el rango verdadero medio en unidades de precio, que utilizamos como motor de volatilidad. En la práctica, utilizamos el valor del ATR para (a) determinar si el mercado presenta un comportamiento demasiado errático o demasiado tranquilo para operar, (b) calcular una distancia dinámica para el stop loss (por ejemplo, stop = ATR * multiplicador) y (c) convertir esa distancia en un importe monetario por lote para poder calcular un tamaño de lote en función del riesgo. El asesor experto creará una instancia de CiATR una sola vez, la actualizará en cada tick, comprobará que su resultado sea válido y utilizará una pequeña función auxiliar para convertir el ATR en pérdida por lote y, a continuación, en un volumen en lotes normalizado según el paso de volumen del bróker.
#include <Indicators\Oscilators.mqh>
CiATR atr;
CiMA: el detector de tendencias
CiMA (de Indicators\Trend.mqh) encapsula la lógica de la media móvil y la gestión del búfer para que usted disponga de valores de media móvil legibles y estables en su asesor experto. Así, utilizamos dos instancias de CiMA (rápida y lenta) para crear un filtro direccional sencillo: cuando la media móvil rápida está por encima de la lenta, se considera que el mercado se encuentra en una tendencia alcista, y viceversa. CiMA también ofrece un acceso sencillo a los valores recientes de la media móvil (de la barra actual o de la última barra cerrada), lo que le permite implementar una lógica de retrocesos (por ejemplo, entrar al mercado cuando el precio retrocede hacia la media móvil rápida en una magnitud equivalente a una fracción del ATR). Al igual que con los demás indicadores, cree la instancia de CiMA una sola vez, actualícela en cada tick, compruebe que los valores no sean EMPTY_VALUE y utilice preferentemente Main(1) para las decisiones al cierre de la barra si desea señales que no se repinten.
#include <Indicators\Trend.mqh>
CiMA fastMA, slowMA;
Ciclo de vida esencial (resumen)
- OnInit() — Llama a Create() una sola vez para cada instancia de clase.
- OnTick() — Llama a Refresh() en cada tick, lea Main() (comprueba que no devuelva EMPTY_VALUE), ejecuta la lógica de decisión, calcula el tamaño de la posición y, a continuación, llama a los métodos de CTrade para ejecutar la operación.
- OnDeinit() — Llama a FullRelease() en los indicadores y elimina los objetos del gráfico
Una vez establecidos estos principios —responsabilidades claras para cada clase, un ciclo de vida disciplinado, un cálculo del tamaño de la posición que tenga en cuenta las unidades y comprobaciones de ejecución robustas—, podemos pasar ahora a la Implementación de un asesor experto conciso y legible que integre CiMA, CiATR y CTrade en el proceso completo de toma de decisiones (Inicializar → Actualizar → Detectar → Calcular el tamaño → Ejecutar → Gestionar) e incluya los mecanismos de seguridad y registro que necesitaremos para realizar pruebas fiables.
Implementación
Ahora que hemos sentado las bases, podemos pasar a la parte práctica de esta exposición: cómo integrar estas clases en una solución de trading funcional. El objetivo aquí no es solo ofrecer descripciones abstractas, sino explicar, paso a paso y de forma clara y estructurada, cómo cada clase —ya sea para la ejecución de operaciones, el análisis de indicadores o la supervisión de cuentas— encaja en la lógica de trading general. Esta sección le guiará desde el concepto hasta el código, garantizando que la transición de la teoría a la práctica sea lógica y accesible.
Encabezado del archivo, archivos de inclusión y parámetros de entrada
Este primer bloque define en qué consiste el programa e incorpora los elementos básicos que utiliza el EA. Además, ofrece una serie de parámetros de entrada para que el lector pueda ajustar los periodos, el riesgo y los límites de seguridad sin necesidad de modificar el código. Mantener los parámetros en la parte superior facilita los experimentos y las pruebas en el aula.
//+------------------------------------------------------------------+ //| CiMA_Trend_Trader.mq5 | //| Compact, working EA that uses CiMA (trend), CiATR (volatility) | //| and CTrade (execution). Adjustable inputs. | //+------------------------------------------------------------------+ #property copyright "Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" #include <Trade\Trade.mqh> #include <Indicators\Trend.mqh> // CiMA #include <Indicators\Oscilators.mqh> // CiATR input int InpFastMAPeriod = 8; // Fast MA period input int InpSlowMAPeriod = 34; // Slow MA period input int InpATRPeriod = 14; // ATR period input double InpRiskPercent = 0.5; // % of balance risk per trade input double InpATRstopMult = 1.5; // SL = ATR * this input double InpPullbackFactor = 0.5; // price within this * ATR of fastMA to consider entry input double InpTakeProfitRR = 2.0; // TP = R * RR input double InpMaxChopPct = 0.006; // skip if ATR/price > this (too choppy) input double InpMinVolPct = 0.0005; // skip if ATR/price < this (too quiet) input double InpMaxDailyLossPct= 2.0; // disable trading for the day if loss exceeds this % input int InpMaxPosPerSym = 1; // max open positions per symbol (basic cap) input int InpMagicNumber = 202507; // EA magic number for labeling input int InpSlippage = 5; // slippage in points (not required for CTrade but useful to log) input bool InpShowTradeLabels = true; // draw arrows/labels on chart
Objetos globales y estado simple
Aquí declaramos los objetos compartidos que el EA utilizará de forma continua: el gestor de operaciones y las instancias de los indicadores. Una pequeña variable de estado registra la hora de la última operación. Estas variables globales persisten durante la inicialización, los ticks y la desinicialización, por lo que la lógica puede reutilizar los identificadores y evitar la creación redundante de handles de indicadores.
// Global objects CTrade trade; CiMA fastMA, slowMA; CiATR atr; // Internal tracking datetime g_lastTradeTime = 0;
Inicialización: creación de indicadores y preparación del EA
Al iniciar el programa, creamos cada indicador una sola vez y comprobamos que se haya creado correctamente. Si falla algún paso del proceso de creación, el EA se detiene y muestra un mensaje claro. Se configura un temporizador con un intervalo breve para realizar tareas de mantenimiento opcionales. Este patrón evita las fugas de recursos y reduce al mínimo el trabajo por tick.
int OnInit() { // Create indicator objects if(!fastMA.Create(_Symbol, _Period, InpFastMAPeriod, 0, MODE_EMA, PRICE_CLOSE)) { Print("[OnInit] fastMA.Create failed for ", _Symbol); return(INIT_FAILED); } if(!slowMA.Create(_Symbol, _Period, InpSlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE)) { Print("[OnInit] slowMA.Create failed for ", _Symbol); return(INIT_FAILED); } if(!atr.Create(_Symbol, _Period, InpATRPeriod)) { Print("[OnInit] atr.Create failed for ", _Symbol); return(INIT_FAILED); } EventSetTimer(1); // optional: ensure OnTimer for housekeeping if needed Print("CiMA_Trend_Trader initialized: ", _Symbol, " ", EnumToString(_Period)); return(INIT_SUCCEEDED); }
Desinicialización y temporizador opcional
Cuando se elimina el EA, liberamos todos los recursos de los indicadores y detenemos el temporizador. El propio manejador del temporizador se deja como un pequeño punto de extensión para realizar diagnósticos o reinicios, por si más adelante desea ampliar el EA.
void OnDeinit(const int reason) { // release indicator resources fastMA.FullRelease(true); slowMA.FullRelease(true); atr.FullRelease(true); EventKillTimer(); } void OnTimer() { // Simple daily reset or housekeeping can be added here if required }
Recuento de posiciones (control de seguridad)
Antes de abrir nuevas operaciones, el EA comprueba cuántas posiciones hay ya abiertas en este símbolo. Esta función analiza las posiciones actuales y devuelve el recuento. Se utiliza para aplicar una regla simple de máximo número de posiciones simultáneas.
int CountOpenPositionsForSymbol() { int total = PositionsTotal(); int count = 0; for(int i=0;i<total;i++) { ulong ticket = PositionGetTicket(i); if(ticket==0) continue; if(!PositionSelectByTicket(ticket)) continue; string sym = PositionGetString(POSITION_SYMBOL); if(sym == _Symbol) count++; } return(count); }
Protección contra pérdidas diarias (protección frente al riesgo)
Para evitar pérdidas descontroladas, esta rutina revisa las operaciones cerradas de hoy y suma las ganancias y pérdidas. Si las pérdidas de hoy superan el porcentaje configurado del saldo de la cuenta, el EA no permite abrir más operaciones. Es un freno de emergencia sencillo pero eficaz.
bool IsDailyLossExceeded() { // compute today's closed profit/loss for the account in account currency double closedPL = 0.0; datetime dayStart = iTime(_Symbol, PERIOD_D1, 0); // start of current day on chart's server timezone ulong from = (ulong)dayStart; int deals = HistoryDealsTotal(); for(int i=deals-1;i>=0;i--) { ulong ticket = HistoryDealGetTicket(i); if(ticket==0) continue; datetime t = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME); if(t < dayStart) continue; double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_COMMISSION) + HistoryDealGetDouble(ticket, DEAL_SWAP); closedPL += profit; } // If closedPL is negative and its absolute exceeds percent of balance, return true double threshold = AccountInfoDouble(ACCOUNT_BALANCE) * (InpMaxDailyLossPct/100.0); if(closedPL < -threshold) return(true); return(false); }
Normalización de lotes (normas del bróker)
Los distintos brókeres establecen el volumen mínimo, el volumen máximo y el incremento permitido del volumen. Esta función auxiliar convierte un volumen en lotes sin normalizar en un volumen válido que cumple con la configuración del símbolo y devuelve cero cuando el volumen calculado es inferior al mínimo establecido por el bróker. La normalización evita que se rechacen órdenes debido a volúmenes no válidos.
double NormalizeLotToStep(double lots) { double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); if(lotStep <= 0) lotStep = 0.01; // fallback if(minLot <= 0) minLot = 0.01; // floor to step double steps = MathFloor(lots / lotStep); double result = steps * lotStep; if(result < minLot) result = 0.0; if(result > maxLot) result = maxLot; // round to reasonable decimals (broker may require 2-3 decimals) int digits = 2; // attempt to infer decimals from lotStep if(lotStep < 0.01) digits = MathMax(0, (int)MathCeil(-MathLog10(lotStep))); result = NormalizeDouble(result, digits); return(result); }
Cálculo del volumen a partir del ATR (cálculo del tamaño de la posición en función del riesgo)
Esta rutina muestra cómo convertir en la práctica una distancia basada en la volatilidad en el volumen de una operación. En primer lugar, calcula la pérdida monetaria por lote para la distancia de stop propuesta y, a continuación, divide el importe de riesgo permitido entre dicha pérdida para obtener el número de lotes. Se incluye una solución alternativa segura para los símbolos que carecen de metadatos sobre el valor del tick.
double CalculateVolume(double stop_distance_price) { if(stop_distance_price <= 0) return(0.0); double risk_amount = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent/100.0); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); if(tick_value <= 0 || tick_size <= 0) { // fallback approximate using point and lot value: (this may be inaccurate for some symbols) double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); if(point<=0 || contract_size<=0) return(0.0); double value_per_price = contract_size; // rough double loss_per_lot = stop_distance_price * value_per_price; if(loss_per_lot <= 0) return(0.0); double lots = risk_amount / loss_per_lot; return NormalizeLotToStep(lots); } double value_per_price = tick_value / tick_size; // money per price unit for 1 lot double loss_per_lot = stop_distance_price * value_per_price; if(loss_per_lot <= 0) return(0.0); double lots = risk_amount / loss_per_lot; double normalized = NormalizeLotToStep(lots); return(normalized); }
Marcado visual de las entradas al mercado
Para mayor claridad durante las pruebas, esta función auxiliar dibuja una flecha y una breve etiqueta en el momento de una entrada al mercado. Las señales visuales resultan de gran ayuda a la hora de relacionar los registros numéricos con los eventos del gráfico y facilitan los experimentos y las pruebas didácticas.
void DrawEntryLabel(bool isLong, datetime when, double price, string tag) { if(!InpShowTradeLabels) return; string objName = tag + "_" + IntegerToString((int)when); int arrow = isLong ? 233 : 234; // Wingdings arrows // create arrow if(!ObjectCreate(0, objName, OBJ_ARROW, 0, when, price)) { // try to remove old and recreate ObjectDelete(0, objName); ObjectCreate(0, objName, OBJ_ARROW, 0, when, price); } ObjectSetInteger(0, objName, OBJPROP_COLOR, isLong ? clrBlue : clrRed); ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrow); ObjectSetString(0, objName, OBJPROP_TEXT, tag); }
Intentos de envío de órdenes (para posiciones largas y cortas)
Estos dos procedimientos encapsulan todo el flujo de comprobación previa → cálculo del tamaño de la posición → ejecución para cada dirección. Realizan comprobaciones de seguridad, calculan el stop y el objetivo, obtienen un volumen normalizado y, a continuación, envían una orden. En caso de fallo, registran el motivo en el log; en caso de éxito, registran la hora y, opcionalmente, marcan el gráfico.
void TryOpenLong(double atr_val) { // safety checks if(CountOpenPositionsForSymbol() >= InpMaxPosPerSym) return; if(IsDailyLossExceeded()) return; double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stop_distance_price = atr_val * InpATRstopMult; if(stop_distance_price<=0) return; double sl = price - stop_distance_price; double tp = price + stop_distance_price * InpTakeProfitRR; double vol = CalculateVolume(stop_distance_price); if(vol <= 0) return; // attempt to place market buy bool ok = trade.Buy(vol, NULL, 0.0, sl, tp, "CiMA_Long_"); if(!ok) { PrintFormat("[TryOpenLong] Buy failed. retcode=%d comment=%s", trade.ResultRetcode(), trade.ResultComment()); return; } // success PrintFormat("[TryOpenLong] Bought %.2f lots at %.5f SL=%.5f TP=%.5f", vol, price, sl, tp); g_lastTradeTime = TimeCurrent(); DrawEntryLabel(true, TimeCurrent(), price, "CiMA_LONG"); } void TryOpenShort(double atr_val) { // safety checks if(CountOpenPositionsForSymbol() >= InpMaxPosPerSym) return; if(IsDailyLossExceeded()) return; double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double stop_distance_price = atr_val * InpATRstopMult; if(stop_distance_price<=0) return; double sl = price + stop_distance_price; double tp = price - stop_distance_price * InpTakeProfitRR; double vol = CalculateVolume(stop_distance_price); if(vol <= 0) return; bool ok = trade.Sell(vol, NULL, 0.0, sl, tp, "CiMA_Short_"); if(!ok) { PrintFormat("[TryOpenShort] Sell failed. retcode=%d comment=%s", trade.ResultRetcode(), trade.ResultComment()); return; } PrintFormat("[TryOpenShort] Sold %.2f lots at %.5f SL=%.5f TP=%.5f", vol, price, sl, tp); g_lastTradeTime = TimeCurrent(); DrawEntryLabel(false, TimeCurrent(), price, "CiMA_SHORT"); }
Proceso de toma de decisiones por tick
Con cada tick de mercado, el asesor experto actualiza sus indicadores, lee los últimos valores, comprueba los filtros básicos de volatilidad y, a continuación, aplica la regla de entrada al mercado en un retroceso. Este bucle central conecta todas las funciones auxiliares: es el núcleo de la estrategia y el punto en el que confluyen los indicadores, el cálculo del tamaño de la posición y la ejecución.
void OnTick() { // refresh indicators (cheap relative to trading operations) // Refresh indicators (the Refresh method returns void in the Standard Library; use flag -1 to update recent data) fastMA.Refresh(-1); slowMA.Refresh(-1); atr.Refresh(-1); // Note: Refresh does not return a status — check indicator values for EMPTY_VALUE below and skip until ready double fast = fastMA.Main(0); double slow = slowMA.Main(0); double atr_val = atr.Main(0); // price units if(fast==EMPTY_VALUE || slow==EMPTY_VALUE || atr_val==EMPTY_VALUE) return; double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); bool uptrend = (fast > slow); bool downtrend = (fast < slow); // normalized volatility check double norm_vol = atr_val / price; if(norm_vol > InpMaxChopPct || norm_vol < InpMinVolPct) { // skip trading in these conditions return; } // Pullback distance between price and fast MA double dist = MathAbs(price - fast); // Only consider entries on bar close for non-repainting behaviour // ensure we use the current completed bar (shift=1) for MA reference if you prefer // for simplicity here we use Main(0) but teachers may show Main(1) alternative if(uptrend && dist <= InpPullbackFactor * atr_val) { TryOpenLong(atr_val); } else if(downtrend && dist <= InpPullbackFactor * atr_val) { TryOpenShort(atr_val); } }
Pequeña utilidad para generar registros legibles
Una pequeña función auxiliar convierte el valor enumerado del marco temporal en una etiqueta de texto breve. Mejora los mensajes registrados y facilita la depuración cuando se trabaja con varios marcos temporales.
string EnumToString(const ENUM_TIMEFRAMES tf) { switch(tf) { case PERIOD_M1: return("M1"); case PERIOD_M5: return("M5"); case PERIOD_M15: return("M15"); case PERIOD_H1: return("H1"); case PERIOD_D1: return("D1"); default: return(IntegerToString((int)tf)); } }
Pruebas y resultados
Una vez compilado, el EA CiMA-Trend-Trader se puede evaluar en el Probador de Estrategias. Durante esta fase, el objetivo no era la rentabilidad inmediata, sino más bien comprobar que el EA funcionara correctamente y se ajustara a su objetivo principal de diseño: seguir las tendencias. En las pruebas, observamos que el asesor experto ofrece resultados más consistentes en marcos temporales de H4 o superiores, en los que las tendencias del mercado son más claras y los sistemas de trading tienden a captarlas con mayor fiabilidad.
Por otro lado, los marcos temporales más cortos suelen presentar estructuras de mercado con un comportamiento errático y sin una tendencia clara, lo que introduce ruido y puede afectar negativamente a los resultados de un enfoque de seguimiento de tendencias como el nuestro. Las siguientes imágenes ilustran mis experiencias con el Probador de Estrategias y ponen de relieve cómo responde el EA en diferentes condiciones de mercado.
Prueba inicial en un gráfico con un marco temporal de 30 minutos

Figura 2. Visualización en el Probador de Estrategias del par GBPUSD en el marco temporal M30 con el EA CiMA_Trend_Trader a partir de junio de 2024

Figura 3. Descenso constante del patrimonio durante la prueba inicial en el marco temporal M30

Figura 4. GBPUSD, M30, resultados de la prueba retrospectiva
La prueba inicial en el marco temporal M30 con el par GBPUSD resultó decepcionante desde el punto de vista de la rentabilidad. A lo largo de una simulación de un año, el EA generó una serie constante de operaciones con pérdidas, y la curva de patrimonio descendió aproximadamente un 50 %. La inspección visual realizada en el Probador de Estrategias reveló la causa: la estructura del mercado del par GBPUSD en el marco temporal M30 presenta numerosos movimientos breves y falsos, así como fases de consolidación con un comportamiento errático y sin una tendencia clara, en las que el sistema entraba repetidamente en señales de tendencia engañosas y las posiciones terminaban cerrándose por stop loss.
En lugar de seguir ajustando intensivamente el sistema en función de esas señales ruidosas, cambié el marco temporal del Probador de Estrategias a uno más amplio —H4— y volví a ejecutar la prueba. Los resultados del H4 que se muestran en la siguiente sección revelan un comportamiento notablemente mejorado; las tendencias son más claras, las señales son menos frecuentes y las operaciones individuales respetan mejor la estructura de mercado de mayor escala.
Prueba en un gráfico con un marco temporal de 4 horas

Figura 5. Ligero aumento del patrimonio durante una prueba en el marco temporal H4

Figura 6. GBPUSD, H4, resultados de la prueba retrospectiva
En los marcos temporales superiores se observó un seguimiento más claro y constante de las tendencias, tal y como confirman los resultados que hemos presentado anteriormente. Esto indica que nuestro sistema de seguimiento de tendencias puede obtener resultados más fiables al filtrar las señales mediante datos de marcos temporales superiores. Mi decisión de pasar a H4 fue una medida adicional destinada a mejorar el rendimiento del sistema y, de hecho, puso de manifiesto las posibles ventajas. Dicho esto, todavía hay muchos parámetros dentro del EA que se pueden ajustar con precisión para mejorar aún más los resultados.
No obstante, operar en marcos temporales más amplios conlleva sus propias ventajas e inconvenientes. Esto exige que el trader tenga paciencia, ya que las señales se producen con menos frecuencia y las posiciones pueden tardar días o incluso semanas en desarrollarse. Por lo tanto, este enfoque resulta más adecuado para traders disciplinados con una perspectiva a largo plazo, más que para los que practican el scalping o para quienes buscan beneficios rápidos y frecuentes. Esta estrategia requiere un capital mayor, ya que permite utilizar un mayor volumen en lotes por operación. Esto, a su vez, aumenta las ganancias totales que pueden acumularse a largo plazo.
A continuación se muestra una imagen (Figura 7) del gráfico H4 en el que se ha operado, mostrando tanto las flechas del historial de operaciones como las de señal. Las tendencias se aprecian claramente, y las medias móviles generadas por la clase CiMA ofrecen una representación suavizada de la dirección del mercado. Esta claridad se debe en gran medida a la elección de un marco temporal superior, lo que, naturalmente, elimina gran parte del ruido que se observa en los marcos temporales inferiores.

Figura 7. GBPUSD, H4—visualización de la tendencia en el Probador de Estrategias
Conclusión
Resulta totalmente factible crear un asesor experto funcional con pocos errores aprovechando la biblioteca estándar de MQL5. Gracias a CTrade, CiMA y CiATR, hemos dado vida a nuestro concepto de seguimiento de tendencias con un código compacto y fácil de leer que ha minimizado las dificultades de desarrollo y ha reducido errores habituales, como la gestión incorrecta de órdenes o la creación redundante de identificadores. Recurriendo a clases de biblioteca probadas, hemos podido centrarnos más en la lógica de la estrategia que en los detalles de implementación de bajo nivel.
En el Probador de Estrategias, el asesor experto ha funcionado sin problemas y nos ha permitido realizar sucesivas iteraciones con rapidez en distintos marcos temporales. Los resultados han mostrado un patrón ya conocido: los marcos temporales más amplios, como H4, permiten aprovechar las tendencias con mayor claridad y solidez, mientras que los marcos temporales más cortos, como M30, sufren falsas rupturas y consolidaciones con mucho ruido. Esto ha puesto de relieve un principio de diseño importante: los sistemas que siguen las tendencias se benefician de la paciencia y de horizontes temporales más amplios, mientras que los enfoques a corto plazo requieren filtros y medidas de protección adicionales para seguir siendo rentables.
Quizá la lección más valiosa ha sido el proceso en sí mismo: pasar del concepto al código y, de ahí, a los resultados. Hemos partido de una idea de trading, la hemos plasmado mediante clases modulares de la biblioteca estándar y también validado su comportamiento mediante la visualización del Probador de Estrategias y los resultados de las pruebas retrospectivas. Este flujo de trabajo sistemático ha dado lugar a un asesor experto funcional y ha reforzado las buenas prácticas de programación, como la separación de responsabilidades, el uso de diagnósticos visuales y la realización de pruebas en distintos regímenes de mercado. Estas prácticas constituyen la base del desarrollo profesional en MQL5.
Para recoger las ideas clave, hemos elaborado una tabla con conclusiones que resumen los principios de programación, las consideraciones estratégicas y las técnicas de prueba que se han extraído de este proyecto. Cada lección está directamente relacionada con retos y soluciones reales de implementación, lo que la convierte en una referencia práctica para los lectores que desean emprender su propio camino en el desarrollo de asesores expertos.
A continuación encontrará la tabla de archivos adjuntos con el archivo fuente del proyecto. El EA CiMA_Trend_Trader.mq5 sirve tanto como demostración práctica como punto de partida reutilizable para los lectores que deseen seguir experimentando. Puede probar, modificar y ampliar el código —quizá integrando otras clases de la biblioteca estándar, como CiCCI o CiMACD, o filtros de volatilidad— para explorar nuevos enfoques y perfeccionar sus propios sistemas de trading.
Le agradeceré sus comentarios y opiniones. Asimismo, le invito a compartir sus experiencias, sugerir mejoras y explorar nuevas vías. ¡Esté atento a nuestra próxima publicación!
Puntos clave
| Lección clave | Descripción: |
|---|---|
| Concepto → Código | Antes de implementar las reglas, tradúzcalas a un algoritmo claro, paso a paso, y a pseudocódigo; esto hace que el comportamiento sea reproducible y más fácil de probar. |
| Estructura de la programación orientada a objetos | Utilice clases o módulos pequeños y especializados (indicadores, cálculo del tamaño de la posición, ejecución) para que cada parte tenga una única responsabilidad y pueda reutilizarse o sustituirse. |
| Gestión del ciclo de vida | Cree las instancias de los indicadores una sola vez, actualícelas en cada tick y libérelas durante la desinicialización para evitar fugas de recursos e inestabilidad en las pruebas. |
| Conversiones de unidades | Especifique claramente las unidades: convierta el ATR (en unidades de precio) a puntos y a pérdida monetaria utilizando el tamaño y valor del tick para garantizar que los niveles de stop y el cálculo del tamaño de la posición sean correctos. |
| Determinación del tamaño de la posición | Haga que la determinación del tamaño de la posición sea un cálculo puro: importe del riesgo ÷ pérdida por lote; a continuación, normalice el resultado según las reglas de volumen del bróker para obtener una exposición reproducible. |
| Ejecución y gestión de errores | Centralice el envío de órdenes, compruebe siempre los códigos y mensajes de resultado e implemente políticas claras de reintento o de respuesta segura ante errores habituales. |
| Control del estado | Mantenga un estado explícito (número de posiciones abiertas, hora de la última operación, indicador de activación) y actualícelo de forma centralizada para evitar condiciones de carrera y desviaciones en la lógica. |
| Codificación defensiva | Prevea la posibilidad de que falten datos, de que los valores del tick sean cero o de que los historiales sean insuficientes; gestione estos casos de forma controlada mediante registros y comportamientos alternativos, en lugar de provocar un fallo del programa. |
| Modularidad | Separe las distintas responsabilidades (señales, cálculo del tamaño de la posición, seguridad, dibujo de objetos gráficos y ejecución) en funciones o archivos para facilitar las pruebas y el mantenimiento. |
| Registro y visualización | Registre los valores utilizados en la toma de decisiones y utilice elementos superpuestos en el gráfico (medias móviles, bandas ATR y marcadores de entrada al mercado) para correlacionar la lógica numérica con el comportamiento visual. |
| Pruebas de alta calidad | Realice pruebas retrospectivas en modo tick con una calidad del 99 % y con spreads y swaps realistas; unos datos deficientes dan lugar a conclusiones engañosas. |
| Disciplina de optimización | Optimice de forma conservadora, utilice la validación fuera de muestra o la validación «walk-forward» y céntrese en las regiones de parámetros estables en lugar de en las ganancias máximas de las pruebas retrospectivas. |
| Atención al rendimiento | Minimice las asignaciones de memoria por tick, reutilice los identificadores y mida el consumo de CPU y memoria al ampliar el sistema a un gran número de símbolos o marcos temporales. |
| Protecciones en tiempo de ejecución | Incluya medidas de seguridad operativas: pérdida máxima diaria, número máximo de posiciones, parada de emergencia y comprobaciones de estado para limitar el riesgo en la operativa en real. |
| Flujo de trabajo de experimentación | Lleve un registro de los experimentos (versiones del código, archivos .set y métricas), realice comparaciones A/B e itere introduciendo cambios pequeños y cuantificables. |
Archivos adjuntos
| Nombre del archivo | Descripción |
|---|---|
| CiMA_Trend_Trader.mq5 | Asesor experto independiente de seguimiento de tendencias. Muestra el uso práctico de tres clases de la biblioteca estándar: CiMA (medias móviles rápidas y lentas para determinar la dirección), CiATR (volatilidad basada en el ATR para stops dinámicos y el cálculo del tamaño de las posiciones) y CTrade (ejecución de órdenes). Incluye parámetros de entrada ajustables (periodos de las medias móviles, periodo y multiplicadores del ATR, porcentaje de riesgo, relación beneficio/riesgo entre el take profit y el stop loss, filtros de volatilidad, pérdida máxima diaria y número máximo de posiciones), normalización de lotes adaptada al bróker, comprobaciones de seguridad, marcadores visuales sencillos de entrada al mercado y un registro claro para la validación en el Probador de Estrategias. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19341
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 crear y optimizar un sistema de trading basado en ciclos (Detrended Price Oscillator - DPO)
Superando las limitaciones del aprendizaje automático (Parte 4): Cómo superar el error irreducible mediante múltiples horizontes de previsión
Algoritmo del Bisonte: Bison Algorithm (BIA)
Redes neuronales en el trading: señales de trading estables en cualquier régimen de mercado (Módulos de atención)
- 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