English Русский 中文 Deutsch 日本語 Português
preview
Integración de MQL5 con paquetes de procesamiento de datos (Parte 1): Análisis avanzado de datos y procesamiento estadístico

Integración de MQL5 con paquetes de procesamiento de datos (Parte 1): Análisis avanzado de datos y procesamiento estadístico

MetaTrader 5Sistemas comerciales | 6 febrero 2025, 12:35
265 0
Hlomohang John Borotho
Hlomohang John Borotho

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.

Opción de gráficos

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.

Datos históricos

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:

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.
Código Python:
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:

Salida (2)

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:

Valores NAN manejados

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.

OPEN

   

HIGH

LOW

CLOSE

TICKVOL

RSI

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.

Mapa de calor (Heatmap)

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:

  Precio actual

Predicción vs. real

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

Archivos adjuntos |
jupyterpackage.ipynb (323.31 KB)
features.txt (0 KB)
Características del Wizard MQL5 que debe conocer (Parte 30): Normalización por lotes en el aprendizaje automático Características del Wizard MQL5 que debe conocer (Parte 30): Normalización por lotes en el aprendizaje automático
La normalización por lotes es el preprocesamiento de datos antes de introducirlos en un algoritmo de aprendizaje automático, como una red neuronal. Esto siempre se hace teniendo en cuenta el tipo de activación que utilizará el algoritmo. Por lo tanto, exploramos los diferentes enfoques que se pueden adoptar para aprovechar los beneficios de esto, con la ayuda de un Asesor Experto ensamblado por un asistente.
Construya Asesores Expertos Auto-Optimizables con MQL5 y Python (Parte II): Ajuste de redes neuronales profundas Construya Asesores Expertos Auto-Optimizables con MQL5 y Python (Parte II): Ajuste de redes neuronales profundas
Los modelos de aprendizaje automático vienen con varios parámetros ajustables. En esta serie de artículos, exploraremos cómo personalizar sus modelos de IA para que se adapten a su mercado específico utilizando la biblioteca SciPy.
Implementación de Deus EA: Trading automatizado con RSI y promedios móviles en MQL5 Implementación de Deus EA: Trading automatizado con RSI y promedios móviles en MQL5
Este artículo describe los pasos para implementar Deus EA basado en los indicadores RSI y promedio móvil para guiar el trading automatizado.
Gestión de Riesgo (Parte 2): Implementando el Cálculo de Lotes en una Interfaz Gráfica Gestión de Riesgo (Parte 2): Implementando el Cálculo de Lotes en una Interfaz Gráfica
En este artículo exploraremos cómo mejorar y aplicar de manera más efectiva los conceptos abordados en el artículo anterior, utilizando las poderosas librerías de controles gráficos de MQL5. Te guiaré paso a paso en la creación de una interfaz gráfica completamente funcional, explicando el plan de diseño detrás de ella, así como el propósito y funcionamiento de cada método empleado. Además, al final del artículo, pondremos a prueba el panel que desarrollaremos, asegurándonos de que funcione correctamente y cumpla con los objetivos planteados.