Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 36): Cómo habilitar el acceso directo desde Python a los flujos de mercado de MetaTrader 5
Índice
- Introducción
- Descripción general de la arquitectura del sistema
- Análisis detallado del backend de Python
- Análisis de la arquitectura del cliente EA de MQL5
- Evaluación y resultados de rendimiento
- Conclusión
Introducción
En nuestro artículo anterior se mostró cómo un sencillo script de MQL5 podía transferir barras históricas a Python, generar características, entrenar un modelo de aprendizaje automático y, a continuación, enviar señales de vuelta a MetaTrader para su ejecución, eliminando así las exportaciones manuales en formato CSV, los análisis basados en Excel y los problemas de control de versiones. Los operadores obtuvieron un proceso integral que transformaba los datos brutos de barras de un minuto en puntos de entrada basados en criterios estadísticos, con niveles de stop-loss (SL) y take-profit (TP) calculados dinámicamente.
Este sistema abordaba tres de los principales problemas del trading algorítmico:
- Fragmentación de datos: se acabó tener que copiar y pegar archivos CSV o lidiar con fórmulas complejas de hojas de cálculo: su gráfico de MetaTrader 5 se comunica directamente con Python.
- Señales tardías: la automatización de la ingeniería de características y la inferencia de modelos permitió obtener señales en tiempo real, pasando de un trading reactivo a uno proactivo basado en datos en tiempo real.
- Gestión del riesgo inconsistente: la incorporación de SL/TP basados en el ATR tanto en las pruebas retrospectivas como en la operativa en tiempo real garantizó que todas las operaciones siguieran reglas ajustadas a la volatilidad, preservando así su ventaja competitiva.

Sin embargo, recurrir a un Asesor Experto (EA) para enviar datos a Python puede generar latencia y complejidad. La nueva versión aprovecha la capacidad de Python para actuar como cliente de MetaTrader 5, utilizando la biblioteca de MetaTrader 5 para obtener y actualizar datos directamente. Este enfoque elimina la espera del temporizador del Asesor Experto; Python puede ingerir datos bajo demanda, escribir de forma eficiente en un almacén Parquet y ejecutar cálculos pesados de forma asíncrona.
Partiendo de esta base, nuestra herramienta híbrida mejorada de Python y MQL5 ofrece unas prestaciones aún mayores:
- Parte de Python: ingesta de datos de MetaTrader 5 en tiempo real a través de la biblioteca nativa, ingeniería avanzada de características (por ejemplo, puntuaciones z de picos, diferencias MACD, bandas ATR, deltas de tendencia de Prophet) y una canalización de Gradient Boosting adaptada a series temporales que se reentrena con ventanas móviles; todo ello expuesto mediante una API ligera de Flask.
- Parte de MQL5: un Asesor Experto robusto basado en sondeos REST con lógica de reintentos, un panel integrado en el gráfico que muestra señales, niveles de confianza y estado de la conexión, marcadores en forma de flecha para entradas y salidas, y ejecución automatizada opcional de órdenes bajo estrictas normas de gestión de riesgos.
Descripción general de la arquitectura del sistema
A continuación se muestra un esquema general de cómo circulan los datos y las señales entre MetaTrader 5 y su servicio de Python, seguido de las funciones principales de cada componente:

Terminal de MetaTrader 5
El terminal de MetaTrader 5 sirve como interfaz principal de mercado y plataforma de gráficos. Contiene y gestiona las barras de precios en tiempo real e históricas del símbolo que usted haya elegido y proporciona el entorno de ejecución para el Asesor Experto (EA). Gracias a su función integrada WebRequest(), el Asesor Experto recopila periódicamente los datos más recientes de barras y muestra las señales entrantes, las líneas de SL/TP y las flechas de entrada y salida directamente en su gráfico. El terminal de MetaTrader 5 se encarga de la colocación de órdenes (cuando está activada), la gestión de objetos locales (paneles, flechas, etiquetas) y la visualización para el usuario de los resultados del sistema.
Fuente de datos de Python
En lugar de recurrir a un Asesor Experto para enviar los datos de las barras, el componente Fuente de datos de Python utiliza la biblioteca oficial de Python para MetaTrader 5 para obtener, bajo demanda, datos OHLC de barras de un minuto tanto históricos como en tiempo real. Inicializa un almacén de datos Parquet comprimido para conservar la evolución de los precios de los últimos días y, a continuación, añade nuevas barras a medida que llegan. Esta configuración elimina la dependencia de los intervalos del temporizador en MQL5 y garantiza que el servicio de Python tenga siempre acceso inmediato y aleatorio al historial completo de precios necesario tanto para los backtests como para la inferencia en tiempo real.
Ingeniería de características
Una vez que las barras sin procesar están disponibles en memoria o en disco, la capa de ingeniería de características las transforma en entradas estadísticamente significativas para el aprendizaje automático. Calcula las puntuaciones z normalizadas de los picos, la diferencia del histograma del MACD, los valores del RSI de 14 períodos, el ATR de 14 períodos para la volatilidad y las bandas de envolvente dinámicas basadas en EMA. Además, utiliza la biblioteca Prophet de Facebook para estimar un delta de tendencia a nivel de minuto, captando el sesgo hacia la reversión a la media frente a la tendencia. Este proceso automatizado garantiza que los datos en tiempo real y los históricos se sometan a un procesamiento idéntico, lo que preserva la fidelidad del modelo.
Modelo de aprendizaje automático
En el núcleo del sistema se encuentra un clasificador Gradient Boosting encapsulado en un Pipeline de scikit-learn con escalado estándar. El modelo se entrena con ventanas deslizantes de barras anteriores, utilizando TimeSeriesSplit para evitar el sesgo de anticipación y RandomizedSearchCV para optimizar los hiperparámetros. Las etiquetas se generan analizando la evolución del precio en los próximos diez minutos y clasificando los movimientos en las categorías «comprar», «vender» o «esperar», en función de un umbral configurable. El estimador entrenado se serializa en model.pkl, lo que garantiza una carga y una inferencia de baja latencia tanto en backtests como en ejecuciones en tiempo real.
API de Flask
La API de Flask actúa como puente entre el ecosistema de ciencia de datos de Python y el Asesor Experto de MQL5. Expone un único endpoint «/analyze» que acepta una carga JSON con los precios de cierre recientes y las marcas de tiempo, aplica el pipeline de características y el modelo cargado para calcular las probabilidades de clase, y devuelve una respuesta JSON concisa que contiene «signal», «sl», «tp» y «conf» (confianza). Esta interfaz REST ligera puede empaquetarse en contenedores o desplegarse en cualquier servidor, lo que desacopla los recursos de cómputo de Python del entorno de ejecución de MetaTrader y simplifica la escalabilidad.
Asesor Experto de MQL5
En el lado del cliente, el Asesor Experto de MQL5 se centra exclusivamente en la interacción con el usuario y la ejecución de operaciones. Consulta periódicamente la API de Flask, analiza el JSON recibido, registra cada señal tanto en la pestaña «Expertos» como en un archivo CSV local, y actualiza un panel gráfico que muestra la señal actual, el nivel de confianza, el estado de la conexión y la marca de tiempo. Cuando se recibe una señal válida de compra, venta o cierre, el EA dibuja flechas y líneas de SL/TP y, si EnableTrading es true, coloca o cierra órdenes mediante la clase CTrade. Al delegar todas las tareas de ciencia de datos a Python, el EA sigue siendo ligero, con buena capacidad de respuesta y fácil de mantener.
Análisis detallado del backend de Python
En la base de nuestro backend hay una sólida canalización de ingesta de datos que utiliza el paquete oficial de Python para MetaTrader 5. La primera vez que se ejecuta, el servicio se «inicializa» recuperando los datos OHLC de barras de un minuto de los últimos días y escribiéndolos en un archivo Parquet comprimido. El formato columnar de Parquet y la compresión Zstandard permiten lecturas ultrarrápidas de segmentos de series temporales, manteniendo al mínimo el uso del disco. A partir de ahí, una sencilla actualización incremental añade únicamente las barras recién formadas, lo que evita descargas redundantes y garantiza que tanto la inferencia en tiempo real como los backtests funcionen sobre una fuente única de referencia, siempre actualizada.
import datetime as dt import pandas as pd import MetaTrader5 as mt5 from pathlib import Path PARQUET_FILE = "hist.parquet.zst" DAYS_TO_PULL = 60 UTC = dt.timezone.utc def bootstrap(): """Fetch last DAYS_TO_PULL days of M1 bars and write to Parquet.""" now = dt.datetime.utcnow() start = now - dt.timedelta(days=DAYS_TO_PULL) mt5.initialize() mt5.symbol_select("Boom 300 Index", True) bars = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, start.replace(tzinfo=UTC), now.replace(tzinfo=UTC)) df = pd.DataFrame(bars) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time').to_parquet(PARQUET_FILE, compression='zstd') def append_new_bars(): """Append only the newest bars since last timestamp.""" df = pd.read_parquet(PARQUET_FILE) last = df.index[-1] now = dt.datetime.utcnow() new = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, last.replace(tzinfo=UTC) + dt.timedelta(minutes=1), now.replace(tzinfo=UTC)) if new: new_df = pd.DataFrame(new) new_df['time'] = pd.to_datetime(new_df['time'], unit='s') merged = pd.concat([df, new_df.set_index('time')]) merged[~merged.index.duplicated()].to_parquet(PARQUET_FILE, compression='zstd')
Con las barras brutas ya disponibles, nuestro pipeline calcula un conjunto de características diseñadas para captar el impulso, la volatilidad y los movimientos extremos. Normalizamos la primera diferencia del precio en una puntuación «z-spike» dividiéndola por su desviación estándar móvil de 20 barras, lo que permite aislar movimientos bruscos del precio. La diferencia del histograma del MACD y el RSI de 14 períodos cuantifican la tendencia y las condiciones de sobrecompra y sobreventa, respectivamente, mientras que el ATR de 14 períodos mide la volatilidad actual. Una media móvil exponencial (EMA) de 20 períodos define unas bandas de envolvente (EMA × 0,997 y EMA × 1,003) que se adaptan a los regímenes cambiantes del mercado. Por último, la biblioteca Prophet de Facebook procesa toda la secuencia de precios de cierre para pronosticar un delta de tendencia a nivel de minuto, lo que permite captar una estacionalidad y una deriva más matizadas y dependientes del tiempo.
import numpy as np import pandas as pd import ta from prophet import Prophet def engineer(df: pd.DataFrame) -> pd.DataFrame: df = df.copy() # z-spike (20-bar rolling std) df['r'] = df['close'].diff() df['z_spike'] = df['r'] / (df['r'].rolling(20).std() + 1e-9) # MACD histogram diff, RSI, ATR df['macd'] = ta.trend.macd_diff(df['close']) df['rsi'] = ta.momentum.rsi(df['close'], window=14) df['atr'] = ta.volatility.average_true_range(df['high'], df['low'], df['close'], window=14) # EMA envelopes ema = df['close'].ewm(span=20).mean() df['env_low'] = ema * 0.997 df['env_up'] = ema * 1.003 # Prophet trend delta (minute-level) if len(df) > 200: m = Prophet(daily_seasonality=False, weekly_seasonality=False) m.fit(pd.DataFrame({'ds': df.index, 'y': df['close']})) df['delta'] = m.predict(m.make_future_dataframe(periods=0, freq='min'))['yhat'] - df['close'] else: df['delta'] = 0.0 return df.dropna()
Planteamos la predicción como una clasificación de tres clases: «BUY» si el precio sube más de un umbral determinado en los próximos 10 minutos, «SELL» si cae más de ese umbral y «WAIT» en caso contrario. Una vez asignadas las etiquetas, las características y las etiquetas se dividen en ventanas temporales móviles para el entrenamiento. Un pipeline de scikit-learn primero estandariza cada característica y, a continuación, ajusta un GradientBoostingClassifier. Los hiperparámetros (tasa de aprendizaje, número de árboles, profundidad máxima) se optimizan mediante RandomizedSearchCV con un esquema de validación cruzada TimeSeriesSplit, lo que garantiza que no haya fuga de información futura. El mejor modelo se serializa en model.pkl, listo para una inferencia inmediata y de baja latencia.
import numpy as np import joblib from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import GradientBoostingClassifier from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV LOOKAHEAD_MIN = 10 LABEL_THRESHOLD = 0.0015 FEATS = ['z_spike','macd','rsi','atr','env_low','env_up','delta'] def label_and_train(df: pd.DataFrame): # Look-ahead return chg = (df['close'].shift(-LOOKAHEAD_MIN) - df['close']) / df['close'] df['label'] = np.where(chg > LABEL_THRESHOLD, 1, np.where(chg < -LABEL_THRESHOLD, 2, 0)) X = df[FEATS].dropna() y = df.loc[X.index, 'label'] pipe = Pipeline([ ('scaler', StandardScaler()), ('gb', GradientBoostingClassifier(random_state=42)) ]) param_dist = { 'gb__learning_rate': [0.01, 0.05, 0.1], 'gb__n_estimators': [300, 500, 700], 'gb__max_depth': [2, 3, 4] } cv = TimeSeriesSplit(n_splits=5) rs = RandomizedSearchCV(pipe, param_dist, n_iter=12, cv=cv, scoring='roc_auc_ovr', n_jobs=-1, random_state=42) rs.fit(X, y) joblib.dump(rs.best_estimator_, 'model.pkl')Para conectar Python y MetaTrader, exponemos un único endpoint de Flask: /analyze. Los clientes envían un payload JSON que contiene el símbolo, un array de precios de cierre y las marcas temporales UNIX correspondientes. El endpoint vuelve a ejecutar nuestro pipeline de generación de características sobre ese payload, carga el modelo preentrenado, calcula las probabilidades de clase, determina la señal con mayor confianza y calcula dinámicamente los niveles de stop-loss y take-profit a partir de la característica ATR. La respuesta es un objeto JSON compacto:
from flask import Flask, request, jsonify import joblib import pandas as pd app = Flask(__name__) model = joblib.load('model.pkl') @app.route('/analyze', methods=['POST']) def analyze(): payload = request.get_json(force=True) closes = payload['prices'] times = pd.to_datetime(payload['timestamps'], unit='s') df = pd.DataFrame({'close': closes}, index=times) # duplicate open/high/low for completeness df[['open','high','low']] = df[['close']] feat = engineer(df).iloc[-1:] probs = model.predict_proba(feat[FEATS])[0] p_buy, p_sell = probs[1], probs[2] signal = ('BUY' if p_buy > 0.55 else 'SELL' if p_sell > 0.55 else 'WAIT') atr = feat['atr'] entry = feat['close'] sl = entry - atr if signal=='BUY' else entry + atr tp = entry + 2*atr if signal=='BUY' else entry - 2*atr return jsonify(signal=signal, sl=round(sl,5), tp=round(tp,5), conf=round(max(p_buy,p_sell),2)) if __name__ == '__main__': app.run(port=5000)
Análisis de la arquitectura del cliente del Asesor Experto en MQL5
El bucle principal del Asesor Experto se ejecuta bien en un evento de temporizador (OnTimer), bien en una comprobación de nueva barra, donde invoca WebRequest() para enviar y recibir mensajes HTTP. En primer lugar, recopila las N barras más recientes mediante CopyRates, convierte el array MqlRates en un payload JSON UTF-8 que contiene el símbolo y la secuencia de precios de cierre, y configura las cabeceras HTTP necesarias. Si WebRequest() falla (devuelve un valor ≤0), el Asesor Experto captura GetLastError(), incrementa un contador de reintentos, registra el error y pospone nuevas solicitudes hasta que se agoten los reintentos o llegue el siguiente tick del temporizador. Las respuestas satisfactorias (estado ≥200) reinician el contador de reintentos y actualizan lastStatus. Este patrón garantiza una recepción robusta y asíncrona de señales sin bloquear el hilo del gráfico ni provocar fallos debido a problemas transitorios de red.
// In OnTimer() or OnNewBar(): MqlRates rates[]; // Copy the last N bars into `rates` if(CopyRates(_Symbol, _Period, 0, InpBufferBars, rates) != InpBufferBars) return; ArraySetAsSeries(rates, true); // Build payload string payload = "{"; payload += StringFormat("\"symbol\":\"%s\",", _Symbol); payload += "\"prices\":["; for(int i=0; i<InpBufferBars; i++) { payload += DoubleToString(rates[i].close, _digits); if(i < InpBufferBars-1) payload += ","; } payload += "]}"; // Send request string headers = "Content-Type: application/json\r\nAccept: application/json\r\n\r\n"; char req[], resp[]; int len = StringToCharArray(payload, req, 0, WHOLE_ARRAY, CP_UTF8); ArrayResize(req, len); ArrayResize(resp, 8192); int status = WebRequest("POST", InpServerURL, headers, "", InpTimeoutMs, req, len, resp, headers); if(status <= 0) { int err = GetLastError(); PrintFormat("WebRequest error %d (attempt %d/%d)", err, retryCount+1, MaxRetry); ResetLastError(); retryCount = (retryCount+1) % MaxRetry; lastStatus = StringFormat("Err%d", err); return; } retryCount = 0; lastStatus = StringFormat("HTTP %d", status);
Una vez analizada una respuesta JSON válida y convertida en una señal, sl y tp, el Asesor Experto actualiza su panel de control en el gráfico y traza cualquier flecha o línea nueva. El panel de control consiste en un único elemento OBJ_RECTANGLE_LABEL con cuatro etiquetas de texto que muestran el símbolo, la señal actual, el estado HTTP y la marca de tiempo. En el caso de las operaciones, elimina cualquier objeto existente con prefijo antes de crear una flecha nueva (OBJ_ARROW) al precio actual, utilizando códigos y colores distintos para las flechas de compra (verde hacia arriba), venta (rojo hacia abajo) o cierre (naranja). Las líneas horizontales (OBJ_HLINE) marcan los niveles de Stop-Loss y Take-Profit, identificados con los colores rojo y verde, respectivamente. Al asignar a cada objeto un espacio de nombres con un prefijo único del gráfico y limpiarlos cuando cambian las señales o durante la desinicialización, su gráfico se mantiene nítido y ordenado.
// Panel (rectangle + labels) void DrawPanel() { const string pid = "SigPanel"; if(ObjectFind(0, pid) < 0) ObjectCreate(0, pid, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, pid, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, pid, OBJPROP_XDISTANCE, PanelX); ObjectSetInteger(0, pid, OBJPROP_YDISTANCE, PanelY); ObjectSetInteger(0, pid, OBJPROP_XSIZE, PanelW); ObjectSetInteger(0, pid, OBJPROP_YSIZE, PanelH); ObjectSetInteger(0, pid, OBJPROP_BACK, true); ObjectSetInteger(0, pid, OBJPROP_BGCOLOR, PanelBG); ObjectSetInteger(0, pid, OBJPROP_COLOR, PanelBorder); string lines[4] = { StringFormat("Symbol : %s", _Symbol), StringFormat("Signal : %s", lastSignal), StringFormat("Status : %s", lastStatus), StringFormat("Time : %s", TimeToString(TimeLocal(), TIME_MINUTES)) }; for(int i=0; i<4; i++) { string lbl = pid + "_L" + IntegerToString(i); if(ObjectFind(0, lbl) < 0) ObjectCreate(0, lbl, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, lbl, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, lbl, OBJPROP_XDISTANCE, PanelX + 6); ObjectSetInteger(0, lbl, OBJPROP_YDISTANCE, PanelY + 4 + i*(TxtSize+2)); ObjectSetString(0, lbl, OBJPROP_TEXT, lines[i]); ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, TxtSize); ObjectSetInteger(0, lbl, OBJPROP_COLOR, TxtColor); } } // Arrows & SL/TP lines void ActOnSignal(ESignal code, double sl, double tp) { // remove old arrows/lines for(int i=ObjectsTotal(0)-1; i>=0; i--) if(StringFind(ObjectName(0,i), objPrefix) == 0) ObjectDelete(0, ObjectName(0,i)); // arrow int arrCode = (code==SIG_BUY ? 233 : code==SIG_SELL ? 234 : 158); color clr = (code==SIG_BUY ? clrLime : code==SIG_SELL ? clrRed : clrOrange); string name = objPrefix + "Arr_" + TimeToString(TimeCurrent(), TIME_SECONDS); ObjectCreate(0, name, OBJ_ARROW, 0, TimeCurrent(), SymbolInfoDouble(_Symbol, SYMBOL_BID)); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrCode); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); // SL line if(sl > 0) { string sln = objPrefix + "SL_" + name; ObjectCreate(0, sln, OBJ_HLINE, 0, 0, sl); ObjectSetInteger(0, sln, OBJPROP_COLOR, clrRed); } // TP line if(tp > 0) { string tpn = objPrefix + "TP_" + name; ObjectCreate(0, tpn, OBJ_HLINE, 0, 0, tp); ObjectSetInteger(0, tpn, OBJPROP_COLOR, clrLime); } }
El envío real de órdenes está condicionado por un indicador EnableTrading, por lo que usted puede alternar fácilmente entre el modo solo visual y el modo de ejecución real. Antes de cualquier orden de mercado, el Asesor Experto comprueba PositionSelect(_Symbol) para evitar posiciones duplicadas. En caso de una señal BUY, el Asesor Experto invoca CTrade.Buy() con FixedLots, SL y TP; en caso de SELL, CTrade.Sell(); y, en caso de una señal CLOSE, CTrade.PositionClose(). La tolerancia al deslizamiento (SlippagePoints) se aplica a las salidas. Esta lógica mínima y con estado garantiza que usted nunca abra accidentalmente dos veces una posición del mismo lado y que todas las órdenes respeten sus parámetros de riesgo predefinidos.
void ExecuteTrade(ESignal code, double sl, double tp) { if(!EnableTrading) return; bool hasPosition = PositionSelect(_Symbol); if(code == SIG_BUY && !hasPosition) trade.Buy(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_SELL && !hasPosition) trade.Sell(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_CLOSE && hasPosition) trade.PositionClose(_Symbol, SlippagePoints); }
Instalación y configuración
Antes de poder empezar a generar señales en tiempo real, tendrá que preparar tanto su entorno de Python como su plataforma MetaTrader 5. Empiece por instalar Python 3.8 o una versión posterior en el mismo ordenador en el que se ejecuta su terminal de MetaTrader 5. Cree y active un entorno virtual (python -m venv venv y, a continuación, en Windows, venv\Scripts\activate, o en macOS/Linux, source venv/bin/activate) e instale todas las dependencias con:En MetaTrader 5, abra
Por ejemplo, (http://127.0.0.1:5000) en la casilla «Permitir WebRequest para las URL indicadas». Este paso de inclusión en la lista de permitidos es fundamental: sin él, MetaTrader 5 descartará silenciosamente todas la cargas POST.
En la carpeta de su proyecto, copie el script del servicio en Python (por ejemplo, market_ai_engine.py) en el directorio de trabajo que elija. Modifique la parte superior del script para configurar su símbolo de trading (MAIN_SYMBOL), sus credenciales de inicio de sesión en MetaTrader 5 (LOGIN_ID, PASSWORD, SERVER) y las rutas de archivo (PARQUET_FILE, MODEL_FILE). Si prefiere utilizar un puerto distinto al predeterminado para el servidor Flask, puede indicarlo mediante --port al iniciar el servicio.
Para desplegar el Asesor Experto, coloque el archivo compilado EA.ex5 (o su archivo fuente .mq5) en su instalación de MetaTrader 5, dentro de MQL5 → Experts.
Reinicie MetaTrader 5 o actualice el Navegador para que el EA aparezca en la lista. Arrástrelo a un gráfico M1 del mismo símbolo que haya configurado en Python. En las entradas del EA, establezca InpServerURL en http://127.0.0.1:5000/analyze, configure InpBufferBars (por ejemplo, 60), InpPollInterval (p. ej., 60 segundos), y InpTimeoutMs (p. ej., 5000 ms). Mantenga EnableTrading desactivado inicialmente para poder verificar las señales sin ejecutar órdenes reales.
Por último, inicie su backend de Python siguiendo esta secuencia:
1. python market_ai_engine.py bootstrap
2. python market_ai_engine.py collect
3. python market_ai_engine.py train
4. python market_ai_engine.py serve --port 5000
Con el servidor Flask en funcionamiento y AutoTrading habilitado en MetaTrader, el EA comenzará a consultar periódicamente señales en vivo, a dibujar flechas y líneas de SL/TP en su gráfico y, cuando usted esté seguro, a abrir operaciones según sus reglas de riesgo predefinidas.
Solución de problemas
Si el Asesor Experto no muestra ningún dato o siempre devuelve «WAIT», compruebe que la URL de su API figure en la lista blanca de la configuración de Asesores Expertos de MetaTrader 5. En entornos mixtos HTTP/HTTPS, utilice HTTP sin cifrar (127.0.0.1) para las pruebas locales y cambie a HTTPS con un certificado de confianza para el entorno de producción. Asegúrese de que los relojes tanto de su servidor como del terminal de MetaTrader 5 estén sincronizados (ya sea ambos en UTC o en la misma zona horaria local) para evitar solicitudes de barras no alineadas. Por último, compruebe que la función «AutoTrading» esté activada y que ningún permiso global ni otros Asesores Expertos estén bloqueando su Asesor Experto.
Evaluación y resultados de rendimiento
Para empezar a trabajar con el sistema híbrido de aprendizaje automático Python–MQL5, el primer paso consiste en inicializar los datos históricos mediante el comando
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py bootstrap 2025-08-04 23:39:23 | INFO | Bootstrapped historical data: 86394 rows
Una vez realizada la inicialización, se pueden mantener las actualizaciones continuas ejecutando
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py collect 2025-08-04 23:41:01 | INFO | Appended 2 new bars 2025-08-04 23:42:01 | INFO | Appended 1 new bars 2025-08-04 23:43:01 | INFO | Appended 1 new bars
Una vez que se dispone de un conjunto de datos actualizado, el siguiente paso es el entrenamiento del modelo.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py train 23:48:44 - cmdstanpy - INFO - Chain [1] start processing 23:51:24 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 02:59:08 | INFO | Model training complete
Para evaluar el rendimiento que podría tener el modelo en operaciones reales, puede ejecutar un backtest utilizando el comando
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py backtest --days 30 06:57:33 - cmdstanpy - INFO - Chain [1] start processing 06:58:30 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 06:59:20 | INFO | Backtest results saved to backtest_results_30d.csv
Resultados del backtest
A continuación se muestra una selección de métricas y detalles de las operaciones extraídos de los resultados del backtest de 30 días:

Gráfico del capital acumulado a lo largo del tiempo

Ahora se presenta un resumen de las métricas clave:
* **Average Entry Price:** 3099.85 * **Average Exit Price:** 3096.53 * **Average PNL (Profit and Loss):** 3.32 * **Total PNL:** 195.69 * **Average Cumulative Equity:** 96.34 * **First Trade Time:** 2025-07-11 14:18:00 * **Last Trade Time:** 2025-07-27 02:00:00
Porcentaje de operaciones ganadoras:
win_rate 72.88135528564453 The win rate is 72.88%. This means that approximately 73% of the trades resulted in a profit.
Una vez que el modelo se haya entrenado y validado, puede iniciar el servidor de inferencia en tiempo real con
2025-08-05 12:41:53 | INFO | analyze: signal=%s, sl=%.5f, tp=%.5f 127.0.0.1 - - [05/Aug/2025 12:41:53] "POST /analyze HTTP/1.1" 200 -
En el lado de MetaTrader, el Asesor Experto debe estar correctamente configurado para consultar el servidor de Python. En los parámetros de entrada del Asesor Experto, debe definirse la URL del servidor (por ejemplo, http://127.0.0.1:5000/analyze), y el Asesor Experto debe adjuntarse al mismo símbolo y al mismo marco temporal con los que se entrenó el modelo, normalmente M1. Una vez en funcionamiento, el Asesor Experto obtendrá señales periódicamente, las mostrará en forma de flechas en el gráfico y, opcionalmente, ejecutará operaciones siguiendo normas estrictas de gestión de riesgos.
2025.08.05 12:41:53.532 trained model (1) (Boom 300 Index,M1) >>> JSON: {"symbol":"Boom 300 Index","prices":[2701.855,2703.124, 2704.408,2705.493,2705.963,2696.806,2698.278,2699.877,2701.464,2702.788,2691.762,2693.046,2694.263,2695.587,2696.863,2698. 179,2699.775,2701.328,2702.888,2698.471,2699.887,2695.534,2696.952,2698.426,2699.756,2699.552,2700.954,2702.131,2703.571, 2699.549,2700.868,2702.567,2703.798,2705.067,2706.874,2698.084,2699.538,2700.856,2702.227,2703.692,2705.102,2706.188,2707.609,2709.001, 2710.335,2711.716,2712.919,2712.028,2713.529,2715.052,2716.578,2717. 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) <<< HTTP 200 hdr: 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) {"conf":0.43,"signal":"WAIT","sl":2725.04317,"tp":2720.18266} 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) [2025.08.05 12:41:53] Signal → WAIT | SL=2725.04317 | TP=2720.18266 | Conf=0.43
Conclusión
En este artículo, hemos creado un Asesor Experto de MQL5 que se apoya en las ventajas de Python en el ámbito de la ciencia de datos. Esto es lo que tiene ahora:- Canalización de datos: su EA participa en el flujo de señales, mientras Python mantiene el almacenamiento en Parquet.
- Ingeniería de características: calcula el «spike-z», el MACD, el RSI, el ATR, las bandas de envolvente e incluso los deltas de tendencia basados en Prophet.
- Modelado y puesta en servicio: usted entrena un modelo de «Gradient Boosting» sensible al tiempo y expone las predicciones a través de Flask.
- Acción en el gráfico: MQL5 utiliza esas señales para trazar flechas y líneas de SL/TP, y puede ejecutar operaciones de forma automática.
Puede probar a sustituir el algoritmo por otro (XGBoost, LSTM, lo que se le ocurra), afinar sus reglas de gestión de riesgos o empaquetar en contenedores el servicio de Python con Docker para conseguir despliegues más limpios. Con esta base, ya tiene todo listo para perfeccionar sus backtests y llevar sus estrategias automatizadas aún más lejos.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19065
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.
Aprendiendo MQL5 de principiante a profesional (Parte VII): Principios de depuración de aplicaciones MQL
Reimaginando las estrategias clásicas (Parte 15): Estrategia de ruptura diaria
Particularidades del trabajo con números del tipo double en MQL4
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 37): Sentiment Tilt Meter
- 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