Desarrollo de un robot en Python y MQL5 (Parte 1): Preprocesamiento de datos
Introducción
El mercado es cada vez más complejo. Hoy se está convirtiendo en una batalla de algoritmos. Más del 95% del volumen de negocio lo generan los robots.
El siguiente paso es el aprendizaje automático. No son IA fuertes, pero tampoco son simples algoritmos lineales. El modelo de aprendizaje automático es capaz de obtener beneficios en condiciones difíciles. Es interesante aplicar el aprendizaje automático para crear sistemas de negociación. Gracias a las redes neuronales, el robot de trading analizará big data, encontrará patrones y predecirá los movimientos de los precios.
Examinaremos el ciclo de desarrollo de un robot de negociación: recopilación de datos, procesamiento, ampliación de muestras, ingeniería de características, selección y formación de modelos, creación de un sistema de negociación mediante Python y supervisión de las operaciones.
Trabajar en Python tiene sus propias ventajas: la velocidad en el campo del aprendizaje automático, así como la capacidad de seleccionar y generar características. La exportación de modelos a ONNX requiere exactamente la misma lógica de generación de características que en Python, lo que no es fácil. Por eso he elegido el comercio en línea a través de Python.
Definir la tarea y elegir una herramienta
El objetivo del proyecto es crear un modelo de aprendizaje automático rentable y sostenible para el comercio en Python. Etapas del trabajo:
- Recopilación de datos de MetaTrader 5, cargando las características principales.
- Aumento de datos para ampliar la muestra.
- Marcado de datos con etiquetas.
- Ingeniería de características: generación, agrupación y selección.
- Selección y entrenamiento del modelo ML. Posiblemente, ensamblar.
- Evaluación de modelos mediante métricas.
- Desarrollo de pruebas para evaluar la rentabilidad.
- Obtener un resultado positivo a partir de nuevos datos.
- Implementación de un algoritmo de negociación mediante Python MQL5.
- Integración con MetaTrader 5.
- Mejora de los modelos.
Herramientas: Python MQL5, librerías ML en Python para mayor velocidad y funcionalidad.
Así pues, hemos definido las metas y los objetivos. En el marco del artículo realizaremos los siguientes trabajos:
- Recopilación de datos de MetaTrader 5, cargando las características principales.
- Aumento de datos para ampliar la muestra.
- Marcado de datos con etiquetas.
- Ingeniería de características: generación, agrupación y selección.
Configuración del entorno y las importaciones. Recogida de datos
En primer lugar, tenemos que obtener datos históricos a través de MetaTrader 5. El código establece una conexión con la plataforma de negociación pasando la ruta al archivo del terminal al método de inicialización.
En el bucle, obtenga los datos utilizando el método mt5.copy_rates_range() con los siguientes parámetros: instrumento, marco temporal, fechas de inicio y fin. Hay un contador de intentos fallidos y un retardo para una conexión estable.
La función termina desconectándose de la plataforma mediante el método mt5.shutdown().
Se trata de una función independiente para su posterior llamada en el programa.
Para ejecutar el script, necesitará instalar las siguientes librerías:
-
NumPy: La librería para trabajar con matrices multidimensionales y funciones matemáticas.
-
Pandas: Herramienta de análisis de datos que proporciona cómodas estructuras de datos para trabajar con tablas y series temporales.
-
Random: Módulo para generar números aleatorios y seleccionar elementos aleatorios de secuencias.
-
Datetime: Proporciona clases y funciones para trabajar con fechas y horas.
-
MetaTrader5: Librería para la interacción con el terminal de comercio MetaTrader 5.
-
Time: Módulo para trabajar con retardos temporales y de ejecución de programas.
-
Concurrent.futures: La herramienta para ejecutar tareas paralelas y asíncronas. Lo necesitaremos en el futuro para el trabajo paralelo multidivisa.
-
Tqdm: Librería para mostrar indicadores de progreso al realizar operaciones iterativas. Lo necesitaremos en el futuro para el trabajo paralelo multidivisa.
-
Train_test_split: Función para dividir un conjunto de datos en conjuntos de entrenamiento y de prueba cuando se entrenan modelos de aprendizaje automático.
Puede instalarlos con 'pip' ejecutando los siguientes comandos:
pip install numpy pandas MetaTrader5 concurrent-futures tqdm sklearn matplotlib imblearn
import numpy as np import pandas as pd import random from datetime import datetime import MetaTrader5 as mt5 import time import concurrent.futures from tqdm import tqdm from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt from sklearn.utils import class_weight from imblearn.under_sampling import RandomUnderSampler # GLOBALS MARKUP = 0.00001 BACKWARD = datetime(2000, 1, 1) FORWARD = datetime(2010, 1, 1) EXAMWARD = datetime(2024, 1, 1) MAX_OPEN_TRADES = 3 symbol = "EURUSD" def retrieve_data(symbol, retries_limit=300): terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe" attempt = 0 raw_data = None while attempt < retries_limit: if not mt5.initialize(path=terminal_path): print("MetaTrader initialization failed") return None instrument_count = mt5.symbols_total() if instrument_count > 0: print(f"Number of instruments in the terminal: {instrument_count}") else: print("No instruments in the terminal") rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_H1, BACKWARD, EXAMWARD) mt5.shutdown() if rates is None or len(rates) == 0: print(f"Data for {symbol} not available (attempt {attempt+1})") attempt += 1 time.sleep(1) else: raw_data = pd.DataFrame(rates[:-1], columns=['time', 'open', 'high', 'low', 'close', 'tick_volume']) raw_data['time'] = pd.to_datetime(raw_data['time'], unit='s') raw_data.set_index('time', inplace=True) break if raw_data is None: print(f"Data retrieval failed after {retries_limit} attempts") return None # Add simple features raw_data['raw_SMA_10'] = raw_data['close'].rolling(window=10).mean() raw_data['raw_SMA_20'] = raw_data['close'].rolling(window=20).mean() raw_data['Price_Change'] = raw_data['close'].pct_change() * 100 # Additional features raw_data['raw_Std_Dev_Close'] = raw_data['close'].rolling(window=20).std() raw_data['raw_Volume_Change'] = raw_data['tick_volume'].pct_change() * 100 raw_data['raw_Prev_Day_Price_Change'] = raw_data['close'] - raw_data['close'].shift(1) raw_data['raw_Prev_Week_Price_Change'] = raw_data['close'] - raw_data['close'].shift(7) raw_data['raw_Prev_Month_Price_Change'] = raw_data['close'] - raw_data['close'].shift(30) raw_data['Consecutive_Positive_Changes'] = (raw_data['Price_Change'] > 0).astype(int).groupby((raw_data['Price_Change'] > 0).astype(int).diff().ne(0).cumsum()).cumsum() raw_data['Consecutive_Negative_Changes'] = (raw_data['Price_Change'] < 0).astype(int).groupby((raw_data['Price_Change'] < 0).astype(int).diff().ne(0).cumsum()).cumsum() raw_data['Price_Density'] = raw_data['close'].rolling(window=10).apply(lambda x: len(set(x))) raw_data['Fractal_Analysis'] = raw_data['close'].rolling(window=10).apply(lambda x: 1 if x.idxmax() else (-1 if x.idxmin() else 0)) raw_data['Price_Volume_Ratio'] = raw_data['close'] / raw_data['tick_volume'] raw_data['Median_Close_7'] = raw_data['close'].rolling(window=7).median() raw_data['Median_Close_30'] = raw_data['close'].rolling(window=30).median() raw_data['Price_Volatility'] = raw_data['close'].rolling(window=20).std() / raw_data['close'].rolling(window=20).mean() print("\nOriginal columns:") print(raw_data[['close', 'high', 'low', 'open', 'tick_volume']].tail(100)) print("\nList of features:") print(raw_data.columns.tolist()) print("\nLast 100 features:") print(raw_data.tail(100)) # Replace NaN values with the mean raw_data.fillna(raw_data.mean(), inplace=True) return raw_data retrieve_data(symbol)
Define variables globales: costos de spread y comisiones del bróker, fechas para las muestras de entrenamiento y prueba, número máximo de operaciones, símbolo.
Carga de cotizaciones de MetaTrader 5 en bucle. Los datos se convierten en un DataFrame y se enriquecen con características:
- Medias móviles SMA de 10 y 20 periodos
- Cambio de precio
- Desviación típica de los precios
- Cambio de volumen
- Variación diaria/mensual de los precios
- Medias de precios
Estas características ayudan a tener en cuenta los factores que influyen en el precio.
Se muestra información sobre las columnas del DataFrame y las últimas entradas.
Veamos cómo se ejecuta nuestra primera función:
Todo funciona. El código ha cargado correctamente las cotizaciones, preparado y cargado las funciones. Pasemos a la siguiente parte del código.
Aumento de datos para ampliar la muestra
El aumento de datos consiste en generar nuevos ejemplos de entrenamiento a partir de los existentes para aumentar el tamaño de la muestra y mejorar la calidad del modelo. Es pertinente para la previsión de series temporales con datos limitados. Además, reduce los errores del modelo y aumenta su robustez.
Métodos de aumento de datos financieros:
- Añadir ruido (desviaciones aleatorias) para aumentar la robustez frente al ruido
- Desplazamiento temporal para modelizar distintos escenarios de desarrollo
- Escalado para modelizar las subidas y bajadas de precios
- Inversión de datos de origen
He implementado la función de aumento de entrada que acepta DataFrame y el número de nuevos ejemplos para cada método. Genera nuevos datos utilizando diferentes enfoques y los concatena con el DataFrame original.
def augment_data(raw_data, noise_level=0.01, time_shift=1, scale_range=(0.9, 1.1)): print(f"Number of rows before augmentation: {len(raw_data)}") # Copy raw_data into augmented_data augmented_data = raw_data.copy() # Add noise noisy_data = raw_data.copy() noisy_data += np.random.normal(0, noise_level, noisy_data.shape) # Replace NaN values with the mean noisy_data.fillna(noisy_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, noisy_data]) print(f"Added {len(noisy_data)} rows after adding noise") # Time shift shifted_data = raw_data.copy() shifted_data.index += pd.DateOffset(hours=time_shift) # Replace NaN values with the mean shifted_data.fillna(shifted_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, shifted_data]) print(f"Added {len(shifted_data)} rows after time shift") # Scaling scale = np.random.uniform(scale_range[0], scale_range[1]) scaled_data = raw_data.copy() scaled_data *= scale # Replace NaN values with the mean scaled_data.fillna(scaled_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, scaled_data]) print(f"Added {len(scaled_data)} rows after scaling") # Inversion inverted_data = raw_data.copy() inverted_data *= -1 # Replace NaN values with the mean inverted_data.fillna(inverted_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, inverted_data]) print(f"Added {len(inverted_data)} rows after inversion") print(f"Number of rows after augmentation: {len(augmented_data)}") # Print dates by years print("Print dates by years:") for year, group in augmented_data.groupby(augmented_data.index.year): print(f"Year {year}: {group.index}") return augmented_data
Llama al código y obtén los siguientes resultados:
Tras aplicar métodos de aumento de datos, nuestro conjunto original de 150.000 barras de precios H1 se amplió a unas impresionantes 747.000 cadenas. Muchos estudios autorizados sobre aprendizaje automático demuestran que un aumento tan significativo del volumen de datos de entrenamiento debido a la generación de ejemplos sintéticos tiene un efecto positivo en las métricas de calidad de los modelos entrenados. Esperamos que en nuestro caso esta técnica también produzca el efecto deseado.
Datos de la etiqueta
El etiquetado de datos es fundamental para el éxito de los algoritmos de aprendizaje supervisado. Nos permite proporcionar a los datos de origen etiquetas que el modelo aprende a continuación. Los datos etiquetados aumentan la precisión, mejoran la generalización, aceleran el entrenamiento y facilitan la evaluación de los modelos. En el problema de previsión del EURUSD, añadimos la columna binaria «etiquetas» que indica si el siguiente cambio de precio fue mayor que el diferencial y la comisión. Esto permite al modelo aprender patrones de repetición de spreads y tendencias de no retroceso.
El etiquetado desempeña un papel fundamental para que los algoritmos de aprendizaje automático encuentren patrones complejos en los datos que no son visibles en su forma bruta. Pasemos a la revisión del código.
def markup_data(data, target_column, label_column, markup_ratio=0.00002): data.loc[:, label_column] = np.where(data.loc[:, target_column].shift(-1) > data.loc[:, target_column] + markup_ratio, 1, 0) data.loc[data[label_column].isna(), label_column] = 0 print(f"Number of markups on price change greater than markup: {data[label_column].sum()}") return data
Ejecute este código y obtenga el número de etiquetas en los datos. La función devuelve un marco. Todo funciona. Por cierto, de más de 700.000 puntos de datos, el precio sólo varió más que el diferencial en 70.000 casos.
Ahora tenemos otra función de marcado de datos. Esta vez, se acerca más a los ingresos reales.
Función de etiquetas de destino
def label_data(data, symbol, min=2, max=48): terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe" if not mt5.initialize(path=terminal_path): print("Error connecting to MetaTrader 5 terminal") return symbol_info = mt5.symbol_info(symbol) stop_level = 100 * symbol_info.point take_level = 800 * symbol_info.point labels = [] for i in range(data.shape[0] - max): rand = random.randint(min, max) curr_pr = data['close'].iloc[i] future_pr = data['close'].iloc[i + rand] min_pr = data['low'].iloc[i:i + rand].min() max_pr = data['high'].iloc[i:i + rand].max() price_change = abs(future_pr - curr_pr) if price_change > take_level and future_pr > curr_pr and min_pr > curr_pr - stop_level: labels.append(1) # Growth elif price_change > take_level and future_pr < curr_pr and max_pr < curr_pr + stop_level: labels.append(0) # Fall else: labels.append(None) data = data.iloc[:len(labels)].copy() data['labels'] = labels data.dropna(inplace=True) X = data.drop('labels', axis=1) y = data['labels'] rus = RandomUnderSampler(random_state=2) X_balanced, y_balanced = rus.fit_resample(X, y) data_balanced = pd.concat([X_balanced, y_balanced], axis=1) return data
La función obtiene etiquetas objetivo para entrenar modelos de aprendizaje automático sobre los beneficios de las operaciones. La función se conecta a MetaTrader 5, recupera la información del símbolo y calcula los niveles de stop/take. A continuación, se determina el precio futuro tras un periodo aleatorio para cada punto de entrada. Si el cambio de precio supera el take profit y no toca el stop, además de cumplir las condiciones de crecimiento/caída, se añade la marca 1,0/0,0 correspondiente. En caso contrario, ninguna. Se crea un nuevo marco de datos con sólo los datos etiquetados. Ninguno se sustituye por promedios.
Se muestra el número de etiquetas de crecimiento/caída.
Todo funciona según lo previsto. Hemos recibido datos y sus derivados. Los datos se han aumentado, completado significativamente y marcado con dos funciones diferentes. Pasemos al siguiente paso: el equilibrio de clases.
Por cierto, creo que los lectores con experiencia en aprendizaje automático hace tiempo que han comprendido que, en última instancia, desarrollaremos un modelo de clasificación, no uno de regresión. Me gustan más los modelos de regresión, ya que veo un poco más de lógica predictiva en ellos que en los modelos de clasificación.
Así que nuestro siguiente paso es el equilibrio de clases.
Clases de equilibrio. Clasificación
La clasificación es un método fundamental de análisis basado en la capacidad humana natural de estructurar la información según características comunes. Una de las primeras aplicaciones de la clasificación fue la prospección, es decir, la identificación de zonas prometedoras a partir de sus características geológicas.
Con la llegada de los ordenadores, la clasificación ha alcanzado un nuevo nivel, pero la esencia permanece: descubrir patrones en el aparente caos de los detalles. Para los mercados financieros, es importante clasificar la dinámica de los precios en crecimiento/caída. Sin embargo, las clases pueden estar desequilibradas: hay pocas tendencias y muchos pisos.
Por lo tanto, se utilizan métodos de equilibrado de clases: eliminación de ejemplos dominantes o generación de ejemplos raros. Esto permite a los modelos reconocer con fiabilidad patrones de dinámica de precios importantes pero poco frecuentes.
pip install imblearn
data = data.iloc[:len(labels)].copy() data['labels'] = labels data.dropna(inplace=True) X = data.drop('labels', axis=1) y = data['labels'] rus = RandomUnderSampler(random_state=2) X_balanced, y_balanced = rus.fit_resample(X, y) data_balanced = pd.concat([X_balanced, y_balanced], axis=1)
Por lo tanto, nuestras clases están ahora equilibradas. Tenemos el mismo número de etiquetas para cada clase (caída de precios y caída).
Pasemos a lo más importante en la previsión de datos: las características.
¿Qué son las características en el aprendizaje automático?
Los atributos y las características son conceptos básicos que todos conocemos desde la infancia. Cuando describimos un objeto, enumeramos sus propiedades: son los atributos. Un conjunto de estas características nos permite caracterizar completamente un objeto.
Lo mismo ocurre en el análisis de datos: cada observación se describe mediante un conjunto de características numéricas y categóricas. La selección de elementos informativos es fundamental para el éxito.
En un nivel superior, tenemos la ingeniería de características, cuando se construyen nuevas características derivadas a partir de los parámetros iniciales para describir mejor el objeto de estudio.
Así, la experiencia humana de conocer el mundo a través de la descripción de los objetos por sus características se traslada a la ciencia en el nivel de la formalización y la automatización.
Extracción automática de características
La ingeniería de características es la transformación de los datos de origen en un conjunto de características para el entrenamiento de modelos de aprendizaje automático. El objetivo es encontrar las características más informativas. Existe un enfoque manual (una persona selecciona las características) y otro automático (mediante algoritmos).
Utilizaremos el enfoque automático. Apliquemos el método de generación de características para extraer automáticamente las mejores características de nuestros datos. A continuación, seleccione las más informativas del conjunto resultante.
Método de generación de nuevos elementos:
def generate_new_features(data, num_features=200, random_seed=1): random.seed(random_seed) new_features = {} for _ in range(num_features): feature_name = f'feature_{len(new_features)}' col1_idx, col2_idx = random.sample(range(len(data.columns)), 2) col1, col2 = data.columns[col1_idx], data.columns[col2_idx] operation = random.choice(['add', 'subtract', 'multiply', 'divide', 'shift', 'rolling_mean', 'rolling_std', 'rolling_max', 'rolling_min', 'rolling_sum']) if operation == 'add': new_features[feature_name] = data[col1] + data[col2] elif operation == 'subtract': new_features[feature_name] = data[col1] - data[col2] elif operation == 'multiply': new_features[feature_name] = data[col1] * data[col2] elif operation == 'divide': new_features[feature_name] = data[col1] / data[col2] elif operation == 'shift': shift = random.randint(1, 10) new_features[feature_name] = data[col1].shift(shift) elif operation == 'rolling_mean': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).mean() elif operation == 'rolling_std': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).std() elif operation == 'rolling_max': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).max() elif operation == 'rolling_min': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).min() elif operation == 'rolling_sum': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).sum() new_data = pd.concat([data, pd.DataFrame(new_features)], axis=1) print("\nGenerated features:") print(new_data[list(new_features.keys())].tail(100)) return data
Por favor, preste atención al siguiente parámetro:
random_seed=42
El parámetro random_seed es necesario para la reproducibilidad de los resultados de la generación de nuevas características. La función generate_new_features crea nuevas características a partir de los datos de origen. Input: datos, número de características, semilla.
Random se inicializa con una semilla dada. En el bucle: se genera un nombre, se seleccionan aleatoriamente 2 características existentes y una operación (suma, resta, etc.). Se calcula una nueva característica para la operación seleccionada.
Tras la generación, se añaden nuevas características a los datos originales. Se devuelven datos actualizados con características generadas automáticamente.
El código nos permite crear automáticamente nuevas funciones para mejorar la calidad del aprendizaje automático.
Lancemos el código y echemos un vistazo al resultado:
100 nuevas características generadas. Pasemos a la siguiente fase: la agrupación de características.
Agrupamiento de características
La agrupación de características agrupa características similares para reducir su número. Esto ayuda a eliminar datos redundantes, reducir la correlación y simplificar el modelo sin sobreajustarlo.
Algoritmos populares: k-means (se especifica el número de clusters, las características se agrupan en torno a los centroides) y clustering jerárquico (estructura de árbol multinivel).
La agrupación de características nos permite eliminar un montón de características inútiles y mejorar la eficacia del modelo.
Veamos el código de agrupación de características:
from sklearn.mixture import GaussianMixture def cluster_features_by_gmm(data, n_components=4): X = data.drop(['label', 'labels'], axis=1) X = X.replace([np.inf, -np.inf], np.nan) X = X.fillna(X.median()) gmm = GaussianMixture(n_components=n_components, random_state=1) gmm.fit(X) data['cluster'] = gmm.predict(X) print("\nFeature clusters:") print(data[['cluster']].tail(100)) return data
Utilizamos el algoritmo GMM (Gaussian Mixture Model) para la agrupación de características. La idea básica es que los datos se generan como una mezcla de distribuciones normales, donde cada distribución es un conglomerado.
En primer lugar, establezca el número de clusters. A continuación, fijamos los parámetros iniciales del modelo: medias, matrices de covarianza y probabilidades de agrupación. El algoritmo recalcula cíclicamente estos parámetros utilizando el método de máxima verosimilitud hasta que dejan de cambiar.
Como resultado, obtenemos los parámetros finales de cada cluster, mediante los cuales podemos determinar a qué cluster pertenece la nueva característica.
GMM es algo genial. Se utiliza en diferentes tareas. Es bueno para datos en los que los conglomerados tienen formas complejas y límites borrosos.
Este código utiliza GMM para dividir las características en clusters. Se toman los datos originales y se eliminan las etiquetas de clase. GMM se utiliza para dividir en un número determinado de clusters. Los números de clúster se añaden como una nueva columna. Al final, se imprime una tabla con los clusters obtenidos.
Ejecutemos el código de agrupación y veamos los resultados:
Pasemos a la función de seleccionar las mejores características.
Seleccionar las mejores características
from sklearn.feature_selection import RFECV from sklearn.ensemble import RandomForestClassifier import pandas as pd def feature_engineering(data, n_features_to_select=10): # Remove the 'label' column as it is not a feature X = data.drop(['label', 'labels'], axis=1) y = data['labels'] # Create a RandomForestClassifier model clf = RandomForestClassifier(n_estimators=100, random_state=1) # Use RFECV to select n_features_to_select best features rfecv = RFECV(estimator=clf, step=1, cv=5, scoring='accuracy', n_jobs=-1, verbose=1, min_features_to_select=n_features_to_select) rfecv.fit(X, y) # Return a DataFrame with the best features, 'label' column, and 'labels' column selected_features = X.columns[rfecv.get_support(indices=True)] selected_data = data[selected_features.tolist() + ['label', 'labels']] # Convert selected_features to a list # Print the table of best features print("\nBest features:") print(pd.DataFrame({'Feature': selected_features})) return selected_data labeled_data_engineered = feature_engineering(labeled_data_clustered, n_features_to_select=10)
Esta función extrae las características más interesantes y útiles de nuestros datos. La entrada son los datos originales con las características de clase y las etiquetas, más el número necesario de características seleccionadas.
En primer lugar, las etiquetas de clase se anulan porque no ayudarán en la selección de características. A continuación, se pone en marcha el algoritmo Random Forest, un modelo que construye un grupo de árboles de decisión sobre conjuntos aleatorios de características.
Después de entrenar todos los árboles, Random Forest evalúa la importancia de cada característica y en qué medida afecta a la clasificación. A partir de estas puntuaciones de importancia, la función selecciona las características más importantes.
Por último, las características seleccionadas se añaden a los datos y se obtienen las etiquetas de clase. La función imprime una tabla con las características seleccionadas y devuelve los datos actualizados.
¿Por qué tanto alboroto? Así nos libramos de funciones basura que sólo ralentizan el proceso. Dejamos las características más frescas, para que el modelo muestre mejores resultados, entrenando con esos datos.
Lancemos la función y veamos los resultados:
El mejor indicador para la previsión de precios resultó ser el propio precio de apertura. La parte superior incluye funciones basadas en medias móviles, incrementos de precios, desviación estándar y variaciones de precios diarias y mensuales. Las características generadas automáticamente resultaron ser poco informativas.
El código permite la selección automática de características importantes, lo que puede mejorar el rendimiento del modelo y su capacidad de generalización.
Conclusión
Hemos creado un código de manipulación de datos que anticipa el desarrollo de nuestro futuro modelo de aprendizaje automático. Repasemos brevemente los pasos dados:
- Los datos iniciales del EURUSD se extrajeron de la plataforma MetaTrader 5. A continuación, se realizaron transformaciones mediante operaciones aleatorias: imposición de ruido, desplazamientos y escalado para aumentar significativamente el tamaño de la muestra.
- El siguiente paso consistió en marcar los valores de los precios con marcadores de tendencia especiales: crecimiento y caída, teniendo en cuenta los niveles de stop loss y take profit. Para equilibrar las clases, se eliminaron los ejemplos redundantes.
- A continuación, se llevó a cabo un complejo procedimiento de ingeniería de rasgos. En primer lugar, se generaron automáticamente cientos de nuevas características derivadas a partir de las series temporales originales. A continuación, se realizó una agrupación de mezclas gaussianas para detectar la redundancia. Por último, se utilizó el algoritmo de random forest para clasificar y seleccionar el subconjunto de variables más informativo.
- Como resultado, se generó un conjunto de datos enriquecidos de alta calidad con características óptimas. Servirá de base para el posterior entrenamiento de modelos de aprendizaje automático y el desarrollo de una estrategia de negociación en Python con integración en MetaTrader 5.
En el próximo artículo, nos centraremos en elegir el modelo de clasificación óptimo, mejorarlo, implementar la validación cruzada y escribir una función de comprobación para el paquete Python/MQL5.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/14350
- 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