
Integración de MQL5 con paquetes de procesamiento de datos (Parte 1): Análisis avanzado de datos y procesamiento estadístico
Introducción
Los mercados financieros generan una gran cantidad de datos, cuyo análisis puede resultar complicado con el único propósito del análisis técnico; bien, el análisis técnico por sí solo, realizado de forma manual, no permite a los operadores analizar e interpretar patrones, tendencias y anomalías dentro de los datos. El paquete de análisis de datos avanzado como Jupyter Lab permite a los operadores realizar análisis estadísticos sofisticados, aprendizaje automático y visualización de datos. Esto ayudará a identificar oportunidades comerciales rentables, comprender el comportamiento del mercado, las tendencias estacionales y predecir movimientos de precios futuros.
Recopilar datos históricos
Para comenzar, necesitamos datos históricos de MetaTrader 5 guardados en formato CSV. Simplemente abre tu plataforma MetaTrader y, en la parte superior del panel de MetaTrader 5, ve a Herramientas y luego a Opciones. Esto te llevará a la configuración de gráficos. Luego tendrás que seleccionar la cantidad de barras del gráfico que deseas descargar. Lo mejor es elegir la opción de barras ilimitadas ya que trabajaremos con fechas y no sabremos cuántas barras hay en un periodo de tiempo determinado.
Después de esto tendrás que descargar los datos reales. Para hacerlo, dirígete a Ver y luego a Símbolos. Esto te llevará a la pestaña de Especificaciones. Desde allí, navega a Barras o Ticks, según el tipo de datos que desees descargar. Proceda e ingrese el periodo de fecha de inicio y fin de los datos históricos que desea descargar, luego haga clic en el botón solicitar para descargar los datos y guardarlos en formato CSV.
Después de todos esos pasos, habrás descargado con éxito los datos históricos en tu plataforma comercial MetaTrader 5. Ahora necesita descargar y configurar el entorno de Jupyter Lab para el análisis. Para descargar y configurar Jupyter Lab, puede dirigirse a su sitio web oficial y siga estos sencillos pasos para descargarlo. Dependiendo del tipo de sistema operativo que uses, tendrás varias opciones para la instalación, ya sea con pip, conda o brew.
Cargar datos históricos de MetaTrader 5 en Jupyter Lab
Para cargar con éxito los datos históricos de MetaTrader 5 en Jupyter Lab, debes conocer la carpeta en la que descargaste los datos. Luego, en Jupyter Lab, simplemente navega hasta esa carpeta. Para comenzar, carga los datos e inspecciona los nombres de las columnas. Tenemos que inspeccionar los nombres de las columnas para manejarlas correctamente y evitar errores que podrían surgir si usamos el nombre de columna incorrecto.
Preprocesamiento
Antes de sumergirnos en el análisis de datos, necesitamos preprocesarlos.
1. Análisis de fecha y hora: convierte la columna de fecha al formato de fecha y hora.
2. Manejo de valores faltantes: maneja cualquier valor faltante.
3. Ingeniería de características: cree nuevas características si es necesario.
Código Python:
import pandas as pd # Load historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path) # Display the first few rows and column names print(data.head()) print(data.columns)
Salida:
En la salida, podemos ver que hay algunos caracteres especiales '<>' dentro de las columnas, así como la presencia de '\t', lo que indica que el archivo está separado por tabulaciones. Ahora podemos proceder a cargar los datos históricos con los nombres de columna correctos.
En el siguiente código, realizaremos lo siguiente:
1. Importando bibliotecas:
- Pandas es una poderosa biblioteca de manipulación de datos en Python.
- La biblioteca de análisis técnico (TA lib) se utiliza para el análisis técnico de datos de los mercados financieros.
2. Cargar datos históricos:
- La ruta del archivo se utiliza para especificar la ubicación del archivo CSV que contiene datos históricos.
- Pd.read lee el archivo CSV en un marco de datos de pandas. El delimitador '\t' indica que el archivo está separado por tabulaciones.
3. Visualización de datos:
- Data.head imprime las primeras filas del marco de datos para verificar el contenido.
- Data.columns imprime los nombres de las columnas para verificar la estructura del marco de datos
4. Ordenar datos por fecha:
- Date.sort ordena el marco de datos en su lugar basándose en la columna '<DATE>'. Esto garantiza que los datos estén en orden cronológico.
5. Cálculo del RSI:
- TA.RSI calcula el Índice de Fuerza Relativa (Relative Strength Index, RSI) utilizando la columna de precios '<CLOSE>'.
- El período de tiempo especifica un RSI de 14 períodos.
- Los valores calculados se almacenan en una nueva columna llamada 'RSI' en el marco de datos.
6. Visualización de los datos actualizados:
- Data.head imprime las primeras filas del marco de datos actualizado para verificar el cálculo del RSI.
import pandas as pd import talib as ta # Load historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t') # Display the first few rows and column names to verify print(data.head()) print(data.columns) # Ensure data is sorted by the correct date column data.sort_values('<DATE>', inplace=True) # Calculate RSI using the '<CLOSE>' price column data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Display the first few rows to verify print(data.head())
Salida:
Desde la salida podemos ver que tenemos valores NAN en la columna RSI, tenemos que manejar los valores NAN correctamente. La función RSI requiere un número mínimo de puntos de datos para calcular el indicador. Podría devolver valores NAN para períodos iniciales. Para manejar correctamente los valores NAN necesitamos saber el tipo de datos de la columna '<CLOSE> '. Asegúrese de que el acceso al marco de datos y a la columna sea correcto. El siguiente código de Python muestra cómo manejar los valores NAN.
import pandas as pd import talib as ta # Load the historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t')data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Verify the column names print(data.columns) # Convert the column to the correct data type if necessary data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Calculate the RSI data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Display the RSI values print(data[['<CLOSE>', 'RSI']].tail(20))
Salida:
Podemos ver que hemos manejado correctamente los valores NAN. Si ve un valor NAN para los períodos iniciales (lo cual es esperado debido al período de bloqueo), puede proceder a manejarlos de la siguiente manera:
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) data['RSI'] = data['RSI'].fillna(0) # or use any other method to handle NaN values
Análisis exploratorio de datos (Exploratory Data Analysis, EDA)
El objetivo principal de EDA es comprender la estructura subyacente de los datos, resumiendo las principales características de los mismos. Al realizar EDA descubrimos patrones e identificamos tendencias y relaciones dentro de los datos. En EDA también detectamos anomalías y valores atípicos o cualquier observación inusual que pueda requerir más investigación. También verificamos suposiciones sobre los datos que podrían afectar el análisis posterior. Luego realizamos una limpieza de datos buscando valores faltantes, errores e inconsistencias en los datos que deban abordarse.
Usamos los siguientes scripts de Python para realizar el Análisis Exploratorio de Datos (EDA):
import seaborn as sns import matplotlib.pyplot as plt import warnings warnings.filterwarnings("ignore") for i in data.select_dtypes(include="number").columns: sns.histplot(data=data, x=i) plt.show()
A partir del código anterior, después de importar todas las bibliotecas necesarias, comenzamos ignorando las advertencias para obtener una salida limpia. A continuación, iteramos por las columnas numéricas, y ' data.select_dtypes(include="number")' devuelve el marco de datos que contiene sólo las columnas numéricas y 'columns' devuelve los nombres de estas columnas. Y luego proceda a trazar el histograma a partir de los datos. Aquí están los histogramas trazados a partir del código anterior.
Luego de las operaciones de procesamiento estadístico, podemos proceder a entrenar el modelo con los datos recopilados. El objetivo es poder hacer predicciones a partir de los datos históricos que recopilamos. Vamos a hacer predicciones utilizando únicamente el indicador RSI. Antes de hacer esto necesitamos saber el tipo de relación que existe entre los datos en sí, eso lo hacemos realizando la matriz de correlación. Deberíamos poder interpretar si la relación existente tiene correlación positiva, correlación negativa o ninguna correlación.
- Correlación positiva: Valores cercanos a 1 indican una fuerte correlación positiva entre dos variables. Por ejemplo, si la correlación entre ‘Abrir’ y ‘Cerrar’ es cercana a 1, significa que se mueven en la misma dirección.
- Correlación negativa: Valores cercanos a -1 indican una fuerte correlación negativa. Por ejemplo, si la correlación entre ‘Volumen’ y ‘Precio’ es cercana a -1, significa que a medida que el volumen aumenta, el precio tiende a disminuir.
- Sin correlación: Valores cercanos a 0 indican que no hay correlación entre las variables.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error import talib as ta # Technical Analysis library # Load the data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t') # Exploratory Data Analysis (EDA) print(data.info()) print(data.describe()) # Visualize the closing price plt.figure(figsize=(12, 6)) plt.plot(data['<CLOSE>']) plt.title('XAUUSD Closing Price') plt.xlabel('<DATE>') plt.ylabel('Price') plt.show() # Feature Engineering data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Drop rows with missing values data.dropna(inplace=True) # Define target variable data['Target'] = data['<CLOSE>'].shift(-1) data.dropna(inplace=True) # Split the data X = data[['RSI']] # Only use RSI as the feature y = data['Target'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False) # Model Development model = RandomForestRegressor(n_estimators=100, random_state=42) model.fit(X_train, y_train) # Predictions y_pred = model.predict(X_test) # Evaluate the model mse = mean_squared_error(y_test, y_pred) print(f'Mean Squared Error: {mse}') # Visualize the predictions plt.figure(figsize=(12, 6)) plt.plot(y_test.index, y_test, label='Actual Values') plt.plot(y_test.index, y_pred, label='Predicted Values') plt.xlabel('Samples') plt.ylabel('TARGET_CLOSE') plt.title('Actual vs Predicted Values') plt.legend() plt.show()
Este es el resultado que obtenemos al entrenar el modelo:
Aunque del análisis de datos podemos ver que los valores previstos no coinciden con los valores reales. Esto indica que el modelo no está capturando eficazmente el patrón subyacente. Varios factores podrían contribuir a esto.
1. Datos de entrenamiento insuficientes: si el conjunto de datos es demasiado pequeño, es posible que el modelo no tenga suficiente información para aprender el significado de la información completa.
2. Sobreajuste: es posible que el modelo haya memorizado los datos de aprendizaje en lugar de aprender a generalizar a partir de ellos. Esto suele ocurrir cuando el modelo es demasiado complejo o no está generalizado adecuadamente.
3. Subajuste: el modelo puede ser demasiado simple para capturar el patrón subyacente en los datos. Esto puede suceder si el modelo no tiene suficiente complejidad o si faltan características importantes.
4. Ingeniería de características: una mala selección de características o una ingeniería insuficiente pueden provocar que el modelo no tenga suficiente información relevante para realizar predicciones.
Pasos para diagnosticar y solucionar el problema:
1. Verifique los datos: asegúrese de que los datos estén limpios, sean consistentes y estén preprocesados correctamente.
2. Evaluar el rendimiento del modelo: utilizar matrices como el error cuadrático medio (Mean Squared Error, MSE) y el error absoluto medio (Mean Absolute Error, MAB).
3. Mejore la ingeniería de funciones: experimente con diferentes funciones, incluidos indicadores técnicos, valores rezagados y otras métricas financieras relevantes.
4. Ajuste los hiperparámetros: utilice técnicas como la búsqueda en cuadrícula o la búsqueda aleatoria para encontrar el hiperparámetro óptimo para su modelo.
Poniéndolo todo junto en MQL5
Después de guardar el modelo, debemos crear un script de Python para realizar predicciones. Este script cargará el modelo y hará predicciones.
import joblib import sys import os
- Joblib: se utiliza para cargar el modelo de aprendizaje automático serializado.
- Sys: que es la abreviatura de 'system' (Sistema) y proporciona acceso a los argumentos de la línea de comandos.
- OS: Es la abreviatura de 'Operating System' (Sistema Operativo) y se utiliza para comprobar la existencia de archivos y realizar operaciones con archivos.
model_path = sys.argv[/home/int_junkie/Documents/ML/random_forest_model.pkl] features_path = sys.argv[/home/int_junkie/Documents/ML/features.txt]
- Sys.argv[1]: El primer argumento de la línea de comandos es la ruta al archivo del modelo.
- Sys.argv[2]: El segundo argumento de la línea de comandos es la ruta al archivo de características.
print(f"Model path: {model_path}") print(f"Features path: {features_path}")
Impresión de información de depuración. Imprime las rutas de los archivos de modelo y características para fines de depuración.
if not os.path.exists(model_path): print(f"Error: Model file not found at {model_path}") sys.exit(1) if not os.path.exists(features_path): print(f"Error: Features file not found at {features_path}") sys.exit(1)
- Comprueba si existe el archivo del modelo y el archivo de características.
- Si alguno de ellos no existe, imprime un mensaje de error y finaliza el script con un código de estado 1.
model = joblib.load(model_path)
- Carga el modelo de aprendizaje automático entrenado desde la ruta especificada en 'model-path'.
with open(features_path, 'r') as f: features = [float(line.strip()) for line in f]
- Abre el archivo de características para su lectura.
- Lee cada línea, elimina los espacios en blanco iniciales o finales, convierte el archivo en un flotante y lo almacena en la lista de 'características'.
prediction = model.predict([features])[0]
- Utiliza el modelo cargado para realizar una predicción basada en las características cargadas.
- Model.predict: Devuelve una lista de predicciones (en este caso una sola predicción), y '[0]' extrae la primera predicción.
print(prediction)
- Imprime la predicción en una salida 'string'. Esta salida puede ser capturada por otro script o programa, como MQL5, para tomar decisiones comerciales.
MQL5
Cargue el modelo en OnInit().
int OnInit(){ // Load the model and feature names string modelPath = "/home/int_junkie/Documents/ML/random_forest_model.pkl"; string featurePath = "/home/int_junkie/Documents/ML/features.txt"; // Your code to load the model (use appropriate library for pkl files) // Initialize the features double features[]; int fileHandle = FileOpen(featurePath, FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string line; while(!FileIsEnding(fileHandle)) { line = FileReadString(fileHandle); ArrayResize(features, ArraySize(features) + 1); features[ArraySize(features) - 1] = StringToDouble(line); } FileClose(fileHandle); } return(INIT_SUCCEEDED); }
Leyendo las predicciones del archivo Python en OnTick().
void OnTick(){ // Declare static variables to retain values across function calls static bool isNewBar = false; static int prevBars = 0; // Get the current number of bars int newbar = iBars(_Symbol, _Period); // Check if the number of bars has changed if (prevBars == newbar) { // No new bar isNewBar = false; } else { // New bar detected isNewBar = true; // Update previous bars count to current prevBars = newbar; } // Update the features based on current data double features[]; ArrayResize(features, 1); features[0] = iClose(Symbol(), 0, 0); // Write the features to a file int fileHandle = FileOpen("/home/int_junkie/Documents/ML/features.txt", FILE_WRITE | FILE_TXT); if (fileHandle != INVALID_HANDLE) { for (int i = 0; i < ArraySize(features); i++) { FileWrite(fileHandle, DoubleToString(features[i])); } FileClose(fileHandle); } else { Print("Error: Cannot open features file for writing"); return; } // Call the Python script to get the prediction string command = "python /home/int_junkie/Documents/ML/predict.py /home/int_junkie/Documents/ML/random_forest_model.pkl /home/int_junkie/Documents/ML/features.txt"; int result = ShellExecuteA(command); if(result != 0) { Print("Error: ShellExecuteA failed with code ", result); return; } // Read the prediction from a file Sleep(1000); // Wait for the Python script to complete fileHandle = FileOpen("/home/int_junkie/Documents/ML/prediction.txt", FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string prediction = FileReadString(fileHandle); FileClose(fileHandle); double pred_value = StringToDouble(prediction); // Generate trading signals based on predictions double some_threshold = 0.0; // Define your threshold if (pred_value > some_threshold) { // Buy signal double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),Digits()); double sl = Ask - stopLoss * _Point; double tp = Ask + takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, Ask, sl, tp, "ML"); } else if (pred_value < some_threshold) { // Sell signal double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),Digits()); double sl = Bid + stopLoss * _Point; double tp = Bid - takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, Bid, sl, tp, "ML"); } } else { Print("Error: Cannot open prediction file for reading"); } }
En OnTick, la variable 'command' prepara un comando de cadena (string) para ejecutar un script de Python con un archivo de características y un archivo de modelo como argumentos. ShellExecuteA (comando) ejecuta el script Python usando ShellExecuteA. 'Sleep (1000)', Espera 1 segundo a que el script Python complete su ejecución. A continuación, abrimos el archivo de predicción para su lectura. Luego verificamos si el archivo de predicción se abrió correctamente. Si es así, lea la predicción. Si no, imprime un error. La variable umbral se utiliza para tomar decisiones comerciales. Si es mayor que el valor previsto, genera una señal de compra. Si es menor que el valor previsto, genera una señal de venta.
Conclusión
En resumen, hemos recopilado datos de la plataforma comercial MetaTrader 5. Los mismos datos que recopilamos luego los usamos en Jupyter Lab para realizar análisis de datos y procesamiento estadístico. Después de realizar el análisis en Jupyter Lab, utilizamos el modelo y lo integramos en MQL5 para tomar decisiones comerciales a partir de los patrones identificados. La integración de MQL5 con Jupyter Lab resuelve el problema de las capacidades analíticas, estadísticas y de visualización limitadas en MQL5. El proceso mejora el desarrollo de estrategias, mejora la eficiencia del manejo de datos y proporciona un entorno colaborativo, flexible y potente para el análisis avanzado de datos y el procesamiento estadístico en el trading.
En conclusión, al integrar MQL5 con Jupyter Lab podemos realizar análisis de datos y procesamiento estadístico avanzados. Con estas capacidades puedes desarrollar cualquier tipo de estrategia comercial y optimizarlas con técnicas avanzadas. Como resultado, proporciona una ventaja competitiva significativa en el campo dinámico e intensivo en datos del comercio financiero.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15155





- 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