
Integración de MQL5 con paquetes de procesamiento de datos (Parte 3): Visualización mejorada de datos
Introducción
Los operadores de los mercados financieros a menudo se enfrentan al reto de dar sentido a grandes cantidades de datos, desde las fluctuaciones de precios y los volúmenes de negociación hasta los indicadores técnicos y las noticias económicas. Con la velocidad y complejidad de los mercados modernos, resulta abrumador interpretar estos flujos de datos de manera eficaz utilizando métodos tradicionales. Los gráficos por sí solos pueden no proporcionar suficiente información, lo que puede llevar a perder oportunidades o a tomar decisiones inoportunas. La necesidad de identificar rápidamente las tendencias, los cambios de tendencia y los riesgos potenciales aumenta la dificultad. Para los operadores que buscan tomar decisiones informadas y basadas en datos, la incapacidad de extraer información clave de los datos es un problema crítico que puede dar lugar a pérdidas de beneficios o a un aumento de los riesgos.
La visualización mejorada de datos aborda este reto transformando los datos financieros sin procesar en representaciones visuales más intuitivas e interactivas. Herramientas como los gráficos dinámicos de velas japonesas, las superposiciones de indicadores técnicos y los mapas de calor de los rendimientos proporcionan a los operadores una comprensión más profunda y práctica de las condiciones del mercado. Al integrar elementos visuales que resaltan tendencias, correlaciones y anomalías, los operadores pueden detectar rápidamente oportunidades y tomar decisiones mejor informadas. Este enfoque mejorado ayuda a reducir la complejidad de la interpretación de los datos, lo que permite a los operadores actuar con mayor confianza y eficiencia en los mercados financieros, que evolucionan rápidamente.
Recopilar datos históricos
from datetime import datetime import MetaTrader5 as mt5 import pandas as pd import pytz # Display data on the MetaTrader 5 package print("MetaTrader5 package author: ", mt5.__author__) print("MetaTrader5 package version: ", mt5.__version__) # Configure pandas display options pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1500) # Establish connection to MetaTrader 5 terminal if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() # Set time zone to UTC timezone = pytz.timezone("Etc/UTC") # Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset utc_from = datetime(2024, 1, 2, tzinfo=timezone) utc_to = datetime.now(timezone) # Set to the current date and time # Get bars from XAUUSD H1 (hourly timeframe) within the specified interval rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to) # Shut down connection to the MetaTrader 5 terminal mt5.shutdown() # Check if data was retrieved if rates is None or len(rates) == 0: print("No data retrieved. Please check the symbol or date range.") else: # Display each element of obtained data in a new line (for the first 10 entries) print("Display obtained data 'as is'") for rate in rates[:10]: print(rate) # Create DataFrame out of the obtained data rates_frame = pd.DataFrame(rates) # Convert time in seconds into the 'datetime' format rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s') # Save the data to a CSV file filename = "XAUUSD_H1_2nd.csv" rates_frame.to_csv(filename, index=False) print(f"\nData saved to file: {filename}")
Para recuperar datos históricos, primero establecemos una conexión con el terminal MetaTrader 5 utilizando la función `mt5.initialize()`. Esto es esencial porque el paquete Python se comunica directamente con la plataforma MetaTrader 5 en ejecución. Configuramos el código para establecer el intervalo de tiempo deseado para la extracción de datos especificando las fechas de inicio y finalización. Los objetos `datetime` se crean en la zona horaria UTC para garantizar la coherencia entre las diferentes zonas horarias. A continuación, el script utiliza la función `mt5.copy-rates-range()` para solicitar datos históricos por hora para el símbolo XAUUSD, desde el 2 de enero de 2024 hasta la fecha y hora actuales.
Después de obtener los datos históricos, nos desconectamos de forma segura del terminal MetaTrader 5 utilizando `mt5.shutdown()` para evitar cualquier conexión innecesaria adicional.. Los datos recuperados se muestran inicialmente en su formato sin procesar para confirmar que la extracción de datos se ha realizado correctamente. Convertimos estos datos en un DataFrame de pandas para facilitar su manipulación y análisis. Además, el código convierte las marcas de tiempo Unix en un formato de fecha y hora legible, lo que garantiza que los datos estén bien estructurados y listos para su posterior procesamiento o análisis. Este enfoque permite a los operadores analizar los movimientos históricos del mercado y tomar decisiones de trading informadas basadas en el rendimiento pasado.
filename = "XAUUSD_H1_2nd.csv" rates_frame.to_csv(filename, index=False) print(f"\nData saved to file: {filename}")
Como mi sistema operativo es Linux, tengo que guardar los datos recibidos en un archivo. Pero para aquellos que utilizan Windows, pueden recuperar los datos fácilmente con el siguiente script:
from datetime import datetime import MetaTrader5 as mt5 import pandas as pd import pytz # Display data on the MetaTrader 5 package print("MetaTrader5 package author: ", mt5.__author__) print("MetaTrader5 package version: ", mt5.__version__) # Configure pandas display options pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1500) # Establish connection to MetaTrader 5 terminal if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() # Set time zone to UTC timezone = pytz.timezone("Etc/UTC") # Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset utc_from = datetime(2024, 1, 2, tzinfo=timezone) utc_to = datetime.now(timezone) # Set to the current date and time # Get bars from XAUUSD H1 (hourly timeframe) within the specified interval rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to) # Shut down connection to the MetaTrader 5 terminal mt5.shutdown() # Check if data was retrieved if rates is None or len(rates) == 0: print("No data retrieved. Please check the symbol or date range.") else: # Display each element of obtained data in a new line (for the first 10 entries) print("Display obtained data 'as is'") for rate in rates[:10]: print(rate) # Create DataFrame out of the obtained data rates_frame = pd.DataFrame(rates) # Convert time in seconds into the 'datetime' format rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s') # Display data directly print("\nDisplay dataframe with data") print(rates_frame.head(10))
Y si por alguna razón no puede obtener los datos históricos, puede recuperarlos manualmente en su plataforma MetTrader 5 siguiendo los siguientes pasos. Inicie su plataforma MetaTrader y, en la parte superior del panel de MetaTrader 5, vaya a > Herramientas y, a continuación, a > Opciones y accederá a las opciones de Gráficos. A continuación, deberá seleccionar el número de barras del gráfico que desea descargar. Es mejor 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 eso, ahora tendrás que descargar los datos reales. Para ello, deberá ir a > Ver y, a continuación, a > Símbolos, y accederá a la pestaña Especificaciones. Simplemente navega a Barras o Ticks dependiendo del tipo de datos que quieras descargar. Continúe e introduzca el periodo de fechas de inicio y finalización de los datos históricos que desea descargar. A continuación, haga clic en el botón de solicitud para descargar los datos y guardarlos en formato CSV.
Visualización de datos de MetaTrader 5 en Jupyter Lab
Para cargar los datos históricos de MetaTrader 5 en Jupyter Lab, primero debe localizar la carpeta donde se descargaron los datos. Una vez en Jupyter Lab, navega hasta esa carpeta para acceder a los archivos. El siguiente paso es cargar los datos y revisar los nombres de las columnas. Es importante revisar los nombres de las columnas para asegurarse de que los datos se gestionan correctamente y evitar errores que podrían producirse por utilizar nombres de columna incorrectos.
Código Python:
import pandas as pd # assign variable to the historical data file_path = '/home/int_junkie/Documents/ML/predi/XAUUSD.m_H1_2nd.csv' data = pd.read_csv(file_path, delimiter='\t') # Display the first few rows and column names print(data.head()) print(data.columns)
Nuestros datos históricos comienzan el 2 de enero de 2024 y llegan hasta la actualidad.
Código Python:
# Convert the <DATE> and <TIME> columns into a single datetime column data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S') # Drop the original <DATE> and <TIME> columns data = data.drop(columns=['<DATE>', '<TIME>']) # Convert numeric columns from strings to appropriate float types numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>'] data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric) # Set datetime as index for easier plotting data.set_index('<DATETIME>', inplace=True) # Let's plot the close price and tick volume to visualize the trend import matplotlib.pyplot as plt # Plot closing price and tick volume fig, ax1 = plt.subplots(figsize=(12, 6)) # Close price on primary y-axis ax1.set_xlabel('Date') ax1.set_ylabel('Close Price', color='tab:blue') ax1.plot(data.index, data['<CLOSE>'], color='tab:blue', label='Close Price') ax1.tick_params(axis='y', labelcolor='tab:blue') # Tick volume on secondary y-axis ax2 = ax1.twinx() ax2.set_ylabel('Tick Volume', color='tab:green') ax2.plot(data.index, data['<TICKVOL>'], color='tab:green', label='Tick Volume') ax2.tick_params(axis='y', labelcolor='tab:green') # Show the plot plt.title('Close Price and Tick Volume Over Time') fig.tight_layout() plt.show()
El gráfico anterior muestra dos métricas clave a lo largo del tiempo:
- Precio de cierre (en azul): Representa el precio de cierre de cada hora en el gráfico. Podemos observar fluctuaciones a lo largo del tiempo, lo que indica períodos de tendencias tanto al alza como a la baja en el precio del oro (XAU/USD).
- Volumen de ticks (en verde): Indica la cantidad de cambios de precio dentro de cada hora. Los picos en el volumen de ticks a menudo corresponden a una mayor actividad o volatilidad del mercado. Por ejemplo, períodos de alto volumen pueden coincidir con movimientos de precios significativos, lo que podría indicar eventos importantes o cambios en el sentimiento del mercado.
Ahora profundicemos en nuestros datos:
1. Análisis de tendencias.
# Calculating moving averages: 50-period and 200-period for trend analysis data['MA50'] = data['<CLOSE>'].rolling(window=50).mean() data['MA200'] = data['<CLOSE>'].rolling(window=200).mean() # Plot close price along with the moving averages plt.figure(figsize=(12, 6)) # Plot close price plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue') # Plot moving averages plt.plot(data.index, data['MA50'], label='50-Period Moving Average', color='orange') plt.plot(data.index, data['MA200'], label='200-Period Moving Average', color='red') plt.title('Close Price with 50 & 200 Period Moving Averages') plt.xlabel('Date') plt.ylabel('Price') plt.legend(loc='best') plt.grid(True) plt.show()
El gráfico muestra el precio de cierre junto con las medias móviles de 50 y 200 períodos:
- Precio de cierre (línea azul): Representa el precio real al final de cada período de tiempo.
- Media móvil de 50 períodos (línea naranja): una media móvil a corto plazo que suaviza los datos de precios a lo largo de 50 períodos. Cuando el precio de cierre cruza por encima de esta línea, puede indicar una posible tendencia alcista, y cuando cruza por debajo, puede indicar una tendencia bajista.
- Media móvil de 200 períodos (línea roja): una media móvil a más largo plazo, que proporciona información sobre la tendencia general. Tiende a reaccionar más lentamente a los cambios de precios, por lo que los cruces con la media móvil de 50 períodos pueden indicar importantes cambios de tendencia a largo plazo.
El punto clave que buscamos es la cruz dorada, cuando la media móvil de 50 períodos cruza por encima de la media móvil de 200 períodos, lo que puede indicar una posible tendencia alcista fuerte. El último es la «cruz de la muerte», que se produce cuando la media móvil de 50 períodos cruza por debajo de la media móvil de 200 períodos, lo que puede indicar una posible tendencia bajista.
A continuación, analizamos la volatilidad calculando el rango de precios (diferencia entre el máximo y el mínimo) y lo visualizamos.
2. Análisis de volatilidad.
# Calculate the price range (High - Low) data['Price_Range'] = data['<HIGH>'] - data['<LOW>'] # Calculate Bollinger Bands # Use a 20-period moving average and 2 standard deviations data['MA20'] = data['<CLOSE>'].rolling(window=20).mean() data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std() data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std() # Plot the price range and Bollinger Bands along with the close price plt.figure(figsize=(12, 8)) # Plot the close price plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue') # Plot Bollinger Bands plt.plot(data.index, data['BB_upper'], label='Upper Bollinger Band', color='red', linestyle='--') plt.plot(data.index, data['BB_lower'], label='Lower Bollinger Band', color='green', linestyle='--') # Fill the area between Bollinger Bands for better visualization plt.fill_between(data.index, data['BB_upper'], data['BB_lower'], color='gray', alpha=0.3) # Plot the price range on a separate axis plt.figure(figsize=(12, 6)) plt.plot(data.index, data['Price_Range'], label='Price Range (High-Low)', color='purple') plt.title('Bollinger Bands and Price Range (Volatility Analysis)') plt.xlabel('Date') plt.ylabel('Price') plt.legend(loc='best') plt.grid(True) plt.show()
A partir de los resultados anteriores, hemos realizado lo siguiente:
- Cálculo del rango de precios: Calculamos el rango de precios para cada periodo de tiempo restando el precio más bajo (`<LOW>`) del precio más alto ('<HIGH>`). Esto indica el alcance de la variación del precio durante cada hora, lo que ayuda a medir la volatilidad de ese periodo.
- `data['Price-Range'] = data['<HIGH>'] - data['<LOW>'] : The resulting `Price-Range` column shows how much the price fluctuated within each hour.
- Cálculo de las Bandas de Bollinger: El indicador de Bandas de Bollinger se calcula para obtener información sobre la volatilidad de los precios. `MA-20` Es la media móvil de 20 períodos de los precios de cierre. Sirve como línea media de las Bandas de Bollinger. `BB-upper` y `BB-lower` representan las bandas superior e inferior, respectivamente. Se calculan como dos desviaciones estándar por encima y por debajo de la media móvil de 20 períodos. Cuando los precios se mueven hacia la banda superior, esto indica que el mercado podría estar sobrecomprado; del mismo modo, los movimientos hacia la banda inferior sugieren que el mercado podría estar sobrevendido.
- Visualización del precio y las bandas de Bollinger: Precio de cierre (línea azul), representa el precio de cierre real para cada período de tiempo. Bandas de Bollinger superior (línea discontinua roja) y Bandas de Bollinger inferior (línea discontinua verde), estas líneas muestran las bandas de volatilidad que forman las Bandas de Bollinger alrededor de la media móvil. El área sombreada, el área entre las Bandas de Bollinger superior e inferior, está sombreada en gris, lo que representa visualmente el rango de volatilidad dentro del cual se espera que se mueva el precio.
- Gráfico de rango de precios separado: El segundo gráfico muestra el rango de precios como una línea morada separada, que muestra cómo fluctúa la volatilidad a lo largo del tiempo. Los picos más grandes en este gráfico indican períodos de mayor volatilidad.
import pandas as pd import talib from stable_baselines3 import DQN from stable_baselines3.common.env_checker import check_env # Verify the environment check_env(env) # Initialize and train the DQN model model = DQN('MlpPolicy', env, verbose=1) model.learn(total_timesteps=10000) # Save the trained model model.save("trading_dqn_model") # Load and preprocess the data as before data = pd.read_csv('XAUUSD_H1_Data-V.csv', delimiter='\t') data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S') data = data.drop(columns=['<DATE>', '<TIME>']) numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>'] data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric) data.set_index('<DATETIME>', inplace=True) # Calculate Bollinger Bands (20-period moving average with 2 standard deviations) data['MA20'] = data['<CLOSE>'].rolling(window=20).mean() data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std() data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std() # Calculate percentage price change and volume change as additional features data['Pct_Change'] = data['<CLOSE>'].pct_change() data['Volume_Change'] = data['<VOL>'].pct_change() # Fill missing values data.fillna(0, inplace=True)
En el código anterior, preparamos datos financieros para un modelo de aprendizaje por refuerzo (Reinforcement Learning, RL) con el fin de tomar decisiones de trading en el mercado XAU/USD (oro) utilizando un algoritmo DQN (Deep Q-Network) de `stable-baseline3`. Primero carga y procesa datos históricos, incluyendo el cálculo de las bandas de Bollinger (un indicador técnico basado en medias móviles y volatilidad de precios) y añade características como los cambios porcentuales en el precio y el volumen. Se valida el entorno y se entrena un modelo DQN durante 100.000 pasos temporales, tras lo cual se guarda el modelo para su uso futuro. Por último, los datos que faltan se rellenan con ceros para garantizar un entrenamiento fluido del modelo.
import gym from gym import spaces import numpy as np class TradingEnv(gym.Env): def __init__(self, data): super(TradingEnv, self).__init__() # Market data and feature columns self.data = data self.current_step = 0 # Define action and observation space # Actions: 0 = Hold, 1 = Buy, 2 = Sell self.action_space = spaces.Discrete(3) # Observations (features: Bollinger Bands, Price Change, Volume Change) self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32) # Initial balance and positions self.balance = 10000 # Starting balance self.position = 0 # No position at the start (0 = no trade, 1 = buy, -1 = sell) def reset(self): self.current_step = 0 self.balance = 10000 self.position = 0 return self._next_observation() def _next_observation(self): # Get the current market data (Bollinger Bands, Price Change, Volume Change) obs = np.array([ self.data['BB_upper'].iloc[self.current_step], self.data['BB_lower'].iloc[self.current_step], self.data['Pct_Change'].iloc[self.current_step], self.data['Volume_Change'].iloc[self.current_step], self.position ]) return obs def step(self, action): # Execute the trade based on action and update balance and position self.current_step += 1 # Get current price current_price = self.data['<CLOSE>'].iloc[self.current_step] reward = 0 # Reward initialization done = self.current_step == len(self.data) - 1 # Check if we're done # Buy action if action == 1 and self.position == 0: self.position = 1 self.entry_price = current_price # Sell action elif action == 2 and self.position == 1: reward = current_price - self.entry_price self.balance += reward self.position = 0 # Hold action else: reward = 0 return self._next_observation(), reward, done, {} def render(self, mode='human', close=False): # Optional: Print the current balance and position print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}") # Create the trading environment env = TradingEnv(data)
A partir del código anterior, definimos una clase de entorno comercial `TradingEnv` usando la biblioteca `gym` para simular un entorno comercial basado en datos históricos del mercado. El entorno permite tres acciones posibles: retener, comprar o vender. Incluye un espacio de observación con cinco características (Bandas de Bollinger, cambio porcentual de precio, cambio de volumen y posición comercial actual). El agente comienza con un saldo de 10.000 unidades y sin posición. En cada paso, en función de la acción seleccionada, el entorno actualiza la posición y el saldo del agente, calcula las recompensas por las operaciones rentables y avanza al siguiente paso en los datos. El entorno puede reiniciarse para iniciar un nuevo episodio o mostrar el estado actual del proceso comercial. Este entorno se utilizará para entrenar modelos de aprendizaje de refuerzo.
import gymnasium as gym from gymnasium import spaces import numpy as np from stable_baselines3 import DQN from stable_baselines3.common.env_checker import check_env # Define the custom Trading Environment class TradingEnv(gym.Env): def __init__(self, data): super(TradingEnv, self).__init__() # Market data and feature columns self.data = data self.current_step = 0 # Define action and observation space # Actions: 0 = Hold, 1 = Buy, 2 = Sell self.action_space = spaces.Discrete(3) # Observations (features: Bollinger Bands, Price Change, Volume Change) self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32) # Initial balance and positions self.balance = 10000 # Starting balance self.position = 0 # No position at the start (0 = no trade, 1 = buy, -1 = sell) def reset(self, seed=None, options=None): # Initialize the random seed self.np_random, seed = self.seed(seed) self.current_step = 0 self.balance = 10000 self.position = 0 # Return initial observation and an empty info dictionary return self._next_observation(), {} def _next_observation(self): # Get the current market data (Bollinger Bands, Price Change, Volume Change) obs = np.array([ self.data['BB_upper'].iloc[self.current_step], self.data['BB_lower'].iloc[self.current_step], self.data['Pct_Change'].iloc[self.current_step], self.data['Volume_Change'].iloc[self.current_step], self.position ], dtype=np.float32) # Explicitly cast to float32 return obs def step(self, action): self.current_step += 1 current_price = self.data['<CLOSE>'].iloc[self.current_step] reward = 0 done = self.current_step == len(self.data) - 1 truncated = False # Set to False unless there's an external condition to end the episode early # Execute the action if action == 1 and self.position == 0: self.position = 1 self.entry_price = current_price elif action == 2 and self.position == 1: reward = current_price - self.entry_price self.balance += reward self.position = 0 # Return next observation, reward, terminated, truncated, and an empty info dict return self._next_observation(), reward, done, truncated, {} def render(self, mode='human', close=False): print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}") def seed(self, seed=None): self.np_random, seed = gym.utils.seeding.np_random(seed) return self.np_random, seed # Assuming your data is already prepared (as a DataFrame) and includes Bollinger Bands and other necessary features # Create the environment env = TradingEnv(data) # Verify the environment check_env(env) # Train the model using DQN model = DQN('MlpPolicy', env, verbose=1) model.learn(total_timesteps=10000) # Save the trained model model.save("trading_dqn_model")
Salida:
Luego definimos un entorno comercial personalizado utilizando "gymnasium" para el aprendizaje de refuerzo, donde un agente aprende a tomar decisiones comerciales basadas en datos históricos del mercado. El entorno permite tres acciones: mantener, comprar o vender, y presenta cinco observaciones, incluidas las bandas de Bollinger, el cambio porcentual de precio, el cambio de volumen y la posición actual. El agente comienza con un saldo de 10.000 y sin posiciones abiertas. Cada paso en el entorno hace avanzar al agente, actualizando su posición, saldo y calculando recompensas por operaciones exitosas. El entorno se valida utilizando la función `check-Env()` de `stable-baseline3`, y se entrena un modelo DQN (Deep Q-Network) durante 10.000 pasos de tiempo para aprender estrategias comerciales óptimas. El modelo entrenado se guarda para uso futuro en sistemas de comercio automatizado.
# Unpack the observation from the reset() method obs, _ = env.reset() # Loop through the environment steps for step in range(len(data)): # Predict the action based on the observation action, _states = model.predict(obs) # Step the environment obs, rewards, done, truncated, info = env.step(action) # Render the environment (print the current state) env.render() # Check if the episode is done if done or truncated: print("Testing completed!") break
El resultado indica que la estrategia de negociación implementada por el modelo entrenado generó una pequeña ganancia durante el período de negociación, con un aumento en el saldo de 10.000$ a 10.108$. Si bien esto sugiere que el modelo fue capaz de identificar operaciones rentables, el margen de beneficio de 108 dólares (una ganancia del 1,08 %) es relativamente modesto.
Los resultados implican que el modelo está realizando operaciones cautelosas o de baja frecuencia, o podrían indicar que la estrategia no está totalmente optimizada para obtener mayores retornos. Una evaluación adicional, que incluya un período de prueba más largo o ajustes a los parámetros del modelo (como la función de recompensa, la selección de características o la lógica de ejecución comercial), podría ayudar a mejorar el rendimiento del modelo. También es importante considerar factores como los costos de transacción y la gestión de riesgos para garantizar que la estrategia siga siendo rentable a lo largo del tiempo.
Juntándolo todo en MQL5
Vamos a conectar MQL5 al script de Python que ejecutará nuestro modelo entrenado, tendremos que configurar un canal de comunicación entre MQL5 y Python. En nuestro caso utilizaremos un servidor de socket, que es el de uso común.//+------------------------------------------------------------------+ //| EnhancedML.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include <WinAPI\winapi.mqh> #include <Trade\Trade.mqh> CTrade trade;
En primer lugar, incluimos la biblioteca API de Windows (`winapi.mqh`) para operaciones a nivel de sistema y la biblioteca comercial (`trade.mqh`) para la gestión comercial. Luego declaramos una instancia de la clase (`CTrade`), llamada trade.
//+------------------------------------------------------------------+ //| Global Vars | //+------------------------------------------------------------------+ int stopLoss = 350; int takeProfit = 500; string Address = "127.0.0.1"; int port = 9999; int socket = SocketCreate(); double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
Establecemos la `Address` en `"127.0.0.1"` (host local) y el `port` en "9999", que usaremos para la comunicación del socket. La función `SocketCreate()` inicializa un socket y almacena el controlador del socket en la variable `socket`, lo que permite la comunicación con el servidor Python.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ if (!SocketConnect(socket, Address, port, 1000)){ Print("Successfully connected to ", Address, ":", port); } else { Print("Connection to ", Address, ":", port, " failed, error ", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
La función `SocketConnect()` intenta conectar el socket creado al servidor Python en la dirección dada (host local) y el puerto `9999`. Nuestro tiempo de espera en milisegundos es `1000`.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ uint len=SocketIsReadable(socket); char buffer[16]; int bytes = SocketRead(socket, buffer, len, 23); if (bytes > 0){ string action_str = CharArrayToString(buffer); int action = StringToInteger(action_str); //int action = atoi(buffer); // Execute a trade based on action if(action == 1){ //buy trade MBuy(); Print("Buy action received..."); } else if(action == 2){ //sell trade MSell(); Print("Sell action received..."); } } }
La función `OnTick()` de nuestro MQL5 está diseñada para procesar las instrucciones de trading entrantes en cada tick del mercado a través de una conexión de socket. Comienza comprobando si hay datos disponibles en el socket utilizando `SocketIsReadable()`, que devuelve la longitud de los datos. Si hay datos disponibles, la función `SocketRead()` lee los datos en un búfer y el número de bytes leídos correctamente se almacena en `bytes`. Si se han recibido datos, el búfer se convierte en una cadena y, a continuación, en un entero (acción). En función del valor de «action», la función ejecuta la operación correspondiente: si «action == 1», se ejecuta una operación de compra llamando a «MBuy()», y si «action == 2», se activa una operación de venta llamando a «MSell()». Después de cada operación, una instrucción de impresión registra la acción recibida (compra o venta). En esencia, la función escucha las órdenes de compra o venta a través del socket y ejecuta automáticamente las operaciones correspondientes.
//+------------------------------------------------------------------+ //| Buy Function | //+------------------------------------------------------------------+ void MBuy(){ static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double Lots = 0.02; double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - stopLoss, digits); double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK) + takeProfit * _Point, digits); trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, Lots, Ask, sl, tp); }
Función para abrir operaciones de compra.
//+------------------------------------------------------------------+ //| Sell Function | //+------------------------------------------------------------------+ void MSell(){ static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double Lots = 0.02; double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + stopLoss, digits); double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID) - takeProfit * _Point, digits); trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, Lots, Bid, sl, tp); }
Función para abrir operaciones de venta.
Script de servidor de socket Python (servidor de modelo de negociación)
Nuestro script Python cargará el modelo entrenado y configurará un servidor socket para escuchar las conexiones desde MQL5. Cuando recibe datos, hará una predicción y enviará la acción comercial.
import socket import numpy as np from stable_baselines3 import DQN # Load the trained model model = DQN.load("trading_dqn_model") # Set up the server HOST = '127.0.0.1' # Localhost (you can replace this with your IP) PORT = 9999 # Port to listen on # Create a TCP/IP socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) print(f"Server listening on {HOST}:{PORT}...") while True: # Wait for a connection client_socket, client_address = server_socket.accept() print(f"Connection from {client_address}") # Receive data from MQL5 (price data sent by EA) data = client_socket.recv(1024).decode('utf-8') if data: print(f"Received data: {data}") # Convert received data to a numpy array observation = np.fromstring(data, sep=',') # Assumes comma-separated price data # Make prediction using the model action, _ = model.predict(observation) # Send the predicted action back to MQL5 client_socket.send(str(action).encode('utf-8')) # Close the client connection client_socket.close()
Guarde el script de Python como `trading-model-server.py` o cualquier nombre de su elección. Abra su terminal o símbolo del sistema, navegue hasta el directorio en el que guarda su modelo y `trading-model-server.py` y ejecute lo siguiente para establecer una conexión.
python trading_model_server.py
Conclusión
En resumen, desarrollamos un sistema comercial integral que integra el aprendizaje automático con MQL5 para automatizar las decisiones comerciales basadas en datos históricos. Comenzamos cargando y preprocesando datos históricos de XAU/USD, calculando las Bandas de Bollinger e implementando otras funciones clave como cambios de precio y volumen. Mediante el aprendizaje por refuerzo, concretamente una red Q profunda (Deep Q-Network, DQN), entrenamos un modelo para predecir acciones de compra y venta basándonos en patrones en los datos. A continuación, el modelo entrenado se conectó a MQL5 a través de un sistema de comunicación por socket, lo que permitió la interacción en tiempo real entre la plataforma de negociación y nuestro modelo de toma de decisiones basado en Python. Esto nos permitió ejecutar operaciones automáticamente en función de las predicciones del modelo, convirtiendo a todo el sistema en una poderosa herramienta para el trading algorítmico.
Esta visualización de datos mejorada y la integración del aprendizaje automático pueden beneficiar significativamente a los comerciantes al proporcionarles conocimientos más profundos y una toma de decisiones más informada. Al analizar las tendencias, la volatilidad y los patrones clave en el mercado, el sistema puede identificar puntos de entrada y salida óptimos para las operaciones. La automatización de la ejecución comercial basada en modelos basados en datos reduce el error humano y el sesgo emocional, lo que conduce a operaciones comerciales más consistentes y estratégicas. En general, este enfoque proporciona a los operadores una herramienta sofisticada que aprovecha los datos históricos para mejorar el rendimiento, al mismo tiempo que ahorra tiempo al automatizar tareas comerciales repetitivas.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16083
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.





- 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
¿dónde enviar los datos de mt5 a python?
No he ejecutado el código, pero parece que es missing.....
¿dónde enviar los datos de mt5 a python?
No he ejecutado el código, pero parece que es missing.....