English Русский 中文 Español Deutsch 日本語
preview
Integração do MQL5 com pacotes de processamento de dados (Parte 3): Visualização de dados aprimorada

Integração do MQL5 com pacotes de processamento de dados (Parte 3): Visualização de dados aprimorada

MetaTrader 5Exemplos |
128 4
Hlomohang John Borotho
Hlomohang John Borotho

Introdução

Os traders dos mercados financeiros frequentemente enfrentam o desafio de interpretar grandes volumes de dados, que incluem flutuações de preços e volumes de operações, indicadores técnicos e notícias econômicas. Dada a velocidade e a complexidade dos mercados modernos, torna-se cada vez mais difícil interpretar esses fluxos de dados de maneira eficaz com métodos tradicionais. Os gráficos, por si só, não conseguem fornecer informações suficientes, o que pode levar à perda de oportunidades ou à tomada de decisões tardias. A necessidade de identificar rapidamente tendências, reversões e riscos potenciais agrava ainda mais a situação. Para os traders que desejam tomar decisões fundamentadas com base em dados, a incapacidade de extrair informações-chave representa um problema sério, que pode resultar em perda de lucros ou aumento do risco.

A visualização de dados aprimorada resolve esse problema, transformando os dados financeiros brutos em uma forma visual mais intuitiva e interativa. Com ferramentas como gráficos de velas dinâmicos, sobreposições de indicadores técnicos e mapas de calor de rentabilidade, os traders conseguem uma compreensão mais aprofundada e prática das condições do mercado. Ao integrar elementos visuais que destacam tendências, correlações e anomalias, os traders conseguem identificar oportunidades rapidamente e tomar decisões mais embasadas. Essa abordagem avançada ajuda a reduzir a complexidade da interpretação dos dados, permitindo que os traders atuem com mais confiança e eficácia em mercados financeiros de rápida movimentação.


Coleta de dados 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 extrair dados históricos, primeiro estabelecemos uma conexão com o terminal MetaTrader 5 por meio da função mt5.initialize(). Isso é essencial, pois o pacote Python interage diretamente com a plataforma MetaTrader 5 em execução. Ajustamos o código para definir o intervalo de tempo desejado para extração dos dados, especificando datas de início e término. Os objetos datetime são criados no fuso horário UTC para garantir consistência entre diferentes fusos. Em seguida, o script utiliza a função mt5.copy_rates_range() para solicitar dados históricos horários para o símbolo XAUUSD, começando em 2 de janeiro de 2024 até a data e hora atuais.

Após obter os dados históricos, desconectamos com segurança do terminal MetaTrader 5 usando mt5.shutdown(), para evitar conexões desnecessárias posteriores. Os dados extraídos são exibidos inicialmente em formato bruto para confirmar que a extração foi bem-sucedida. Convertendo esses dados em um pandas DataFrame, conseguimos facilitar o processamento e a análise. Além disso, o código transforma os carimbos de tempo Unix em um formato de data e hora legível, garantindo que os dados estejam bem estruturados e prontos para processamento ou análise. Essa abordagem permite que os traders analisem os movimentos históricos do mercado e tomem decisões de negociação embasadas com base nos resultados passados.

filename = "XAUUSD_H1_2nd.csv"
rates_frame.to_csv(filename, index=False)
print(f"\nData saved to file: {filename}")

Como meu sistema operacional é Linux, preciso salvar os dados obtidos em um arquivo. Usuários do Windows podem obter os dados com o seguinte 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))

Se por algum motivo você não conseguir extrair os dados históricos, é possível extraí-los manualmente na plataforma MetaTrader 5, seguindo os passos a seguir. Inicie a plataforma MetaTrader e, na parte superior do painel, selecione "Serviço" > "Configurações" > aba "Gráficos". Em seguida, será necessário escolher o número de barras no gráfico que você deseja carregar. O ideal é selecionar a opção com número ilimitado de barras, pois trabalharemos com datas e não saberemos quantas barras existem em determinado intervalo de tempo.

Depois disso, carregaremos os dados reais. Vá em "Exibir" > "Símbolos" > aba "Especificação". Vá até a aba "Barras" ou "Ticks", dependendo dos dados que deseja carregar. Insira as datas de início e fim do período dos dados históricos que deseja baixar, depois clique em "Solicitar" para carregar os dados e salvá-los no formato .csv.


Visualização de dados do MetaTrader 5 no Jupyter Lab

Para carregar os dados históricos do MetaTrader 5 no Jupyter Lab, você primeiro precisa localizar a pasta onde esses dados foram baixados. Estando no Jupyter Lab, navegue até essa pasta para acessar os arquivos. O próximo passo será carregar os dados e verificar os nomes das colunas. Verificar os nomes das colunas é importante para garantir o gerenciamento correto dos dados e evitar erros que possam ocorrer devido ao uso incorreto desses nomes.

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)



Nossos dados históricos cobrem o período de 2 de janeiro de 2024 até o momento atual.

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()

preço de fechamento e volume em ticks

No gráfico acima está mostrada a dinâmica de dois indicadores-chave ao longo do tempo:

  1. Close Price (linha azul) – o preço de fechamento para cada hora no gráfico. Podemos observar oscilações ao longo do tempo, indicando períodos de tendência de alta e de baixa no preço do ouro (XAU/USD).
  2. Tick Volume (barras verdes) – a quantidade de alterações de preço durante cada hora. Picos no volume em ticks geralmente correspondem a uma atividade elevada ou a maior volatilidade no mercado. Por exemplo, períodos de alto volume podem coincidir com oscilações significativas de preço, o que pode sinalizar eventos importantes ou mudanças no sentimento do mercado.

Agora vamos analisar nossos dados com mais detalhes:

1. Análise de tendência.

# 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()


No gráfico, temos o preço de fechamento junto com as médias móveis de 50 e 200 períodos:

  1. Close Price (linha azul) – o preço real no final de cada período de tempo.
  2. 50-period Moving Average (linha laranja) – média móvel de curto prazo que suaviza os dados de preço ao longo de 50 períodos. Quando o preço de fechamento cruza essa linha para cima, isso pode ser um sinal de uma possível tendência de alta; quando cruza para baixo, pode indicar uma tendência de baixa.
  3. 200-period Moving Average (linha vermelha) – média móvel de 200 períodos mais longa, que oferece uma visão do panorama geral da tendência. Ela tende a reagir mais lentamente às mudanças de preço, por isso os cruzamentos com a média de 50 períodos podem sinalizar reversões importantes de tendência no longo prazo.

O elemento-chave que buscamos é o cruzamento dourado: quando a média móvel de 50 períodos cruza a de 200 períodos para cima, isso pode sinalizar uma tendência de alta forte e potencial. Há também o cruzamento da morte, quando a média de 50 períodos cai abaixo da de 200. Isso pode indicar uma possível tendência de baixa.

Em seguida, analisamos a volatilidade, calculando a faixa de preço (diferença entre a máxima e a mínima) e visualizando-a.

2. Análise de volatilidade.

# 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()



Com base na saída acima, fizemos o seguinte:

  1. Cálculo da faixa de preço: Calculamos a faixa de preço para cada período subtraindo o preço mínimo (`<LOW>`) do preço máximo ('<HIGH>`). Isso fornece uma noção da variação do preço ao longo de cada hora, ajudando a medir a volatilidade nesse intervalo.
  2. `data['Price-Range'] = data['<HIGH>'] - data['<LOW>']: A coluna resultante, Price-Range (faixa de preço), mostra o quanto o preço oscilou durante cada hora.
  3. Cálculo das Bandas de Bollinger: O indicador Bandas de Bollinger é calculado para analisar a volatilidade dos preços. MA-20 – a média móvel de 20 períodos do preço de fechamento. Ela funciona como linha média das Bandas de Bollinger. BB-upper e BB-lower representam as bandas superior e inferior, respectivamente. Elas são calculadas como dois desvios padrão acima e abaixo da média móvel de 20 períodos. Quando os preços se aproximam da banda superior, isso pode indicar que o mercado está sobrecomprado; da mesma forma, o movimento em direção à banda inferior sugere que o mercado pode estar sobrevendido.
  4. Visualização do preço e das Bandas de Bollinger: o preço de fechamento (linha azul) representa o valor real de fechamento em cada período. Bandas superiores de Bollinger (linha vermelha tracejada) e bandas inferiores de Bollinger (linha verde tracejada), essas linhas mostram os limites de volatilidade que formam as Bandas de Bollinger em torno da média móvel. A área sombreada — a região entre as bandas superior e inferior — é preenchida em cinza, representando visualmente o intervalo de volatilidade esperado dentro do qual o preço deverá se mover.
  5. Gráfico separado da faixa de preço (Price Range): O segundo gráfico exibe a faixa de preço como uma linha roxa separada, mostrando como a volatilidade varia ao longo do tempo. Picos maiores neste gráfico indicam períodos de maior volatilidade.
Criação de modelo de aprendizado por reforço:
    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)

    No código acima, preparamos os dados financeiros para o modelo de aprendizado por reforço (reinforcement learning, RL) a fim de tomar decisões de negociação no mercado XAUUSD (ouro), utilizando o algoritmo Deep Q-Network (DQN) da biblioteca stable-baseline3. Primeiro, ele carrega e processa os dados históricos, incluindo o cálculo das Bandas de Bollinger (um indicador técnico baseado em médias móveis e volatilidade de preços), além de adicionar variáveis como variação percentual de preço e de volume. O ambiente é verificado, e o modelo DQN é treinado por 10.000 passos temporais, após os quais o modelo é salvo para uso futuro. Por fim, os dados ausentes são preenchidos com zeros para garantir um treinamento contínuo e estável do 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)

    No código acima, definimos a classe do ambiente de negociação TradingEnv, usando a biblioteca gym para simular um ambiente de trading baseado em dados históricos de mercado. O ambiente permite três ações possíveis: manter, comprar ou vender. Ele inclui um espaço de observação com cinco variáveis (Bandas de Bollinger, variação percentual do preço, variação do volume e a posição atual de negociação). O agente começa com um saldo de 10.000 unidades e sem posição. A cada etapa, dependendo da ação escolhida, o ambiente atualiza a posição e o saldo do agente, calcula as recompensas com base em operações lucrativas e avança para a próxima etapa do processamento de dados. O ambiente pode ser reiniciado para iniciar um novo episódio ou exibir o estado atual do processo de negociação. Esse ambiente será utilizado para o treinamento de modelos de aprendizado por reforço.

    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")
    

    Resultado:

    Modelo

    Em seguida, definimos o ambiente de negociação personalizado usando o gymnasium para aprendizado por reforço, onde o agente aprende a tomar decisões de negociação com base em dados históricos de mercado. O ambiente permite três ações: manter, comprar ou vender, e inclui cinco variáveis de observação, como Bandas de Bollinger, variação percentual do preço, variação do volume e a posição atual. O agente começa com um saldo de 10.000 e sem posições abertas. A cada passo no ambiente, o agente avança, atualizando sua posição, saldo e calculando as recompensas por operações bem-sucedidas. O ambiente é validado com a função check_Env() da stable-baseline3, e o modelo DQN (Deep Q-Network) é treinado por 10.000 passos temporais para aprender estratégias de negociação ideais. O modelo treinado é salvo para uso futuro em sistemas de negociação automatizados.

    # 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
    

    Equity inicial

    Equity final

    O resultado mostra que a estratégia de negociação implementada pelo modelo treinado resultou em um pequeno lucro durante o período de negociação, com o saldo aumentando de USD 10.000 para USD 10.108. Embora isso indique que o modelo foi capaz de identificar operações lucrativas, a margem de lucro de USD 108 (um aumento de 1,08%) é relativamente modesta.

    Os resultados sugerem que o modelo realiza operações de forma cautelosa ou em baixa frequência, ou pode indicar que a estratégia ainda não está completamente otimizada para gerar lucros maiores. Avaliações adicionais, incluindo um período de teste mais longo ou ajustes nos parâmetros do modelo (como função de recompensa, seleção de características ou lógica de execução das ordens), podem ajudar a melhorar o desempenho do modelo. Também é importante levar em conta fatores como custos operacionais e gerenciamento de risco para garantir que a estratégia permaneça lucrativa ao longo do tempo.

    Integrando tudo no MQL5

    Vamos conectar o MQL5 ao script Python que executará nosso modelo treinado. Para isso, precisaremos configurar um canal de comunicação entre o MQL5 e o Python. No nosso caso, utilizaremos um servidor de sockets amplamente utilizado.
    //+------------------------------------------------------------------+
    //|                                                   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;

    Primeiro, conectamos a biblioteca Windows API (winapi.mqh) para operações em nível de sistema e a biblioteca de negociação (trade.mqh) para gerenciamento das ordens. Em seguida, declaramos uma instância da classe (CTrade) com o nome 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);

    Definimos o Address como "127.0.0.1" (localhost) e o port como "9999" para a comunicação entre os sockets. A função SocketCreate() inicializa o socket e armazena o identificador do socket na variável socket, garantindo a comunicação com o 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);
    }

    A função SocketConnect() tenta conectar o socket criado ao servidor Python no endereço especificado (localhost) e na porta 9999. Nosso tempo de espera em milissegundos é definido como 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...");
          }
       }
       
    }

    A função OnTick() no MQL5 é responsável por processar instruções de negociação recebidas a cada novo tick do mercado através da comunicação via socket. Ela começa verificando se há dados disponíveis no socket com a função SocketIsReadable(), que retorna o tamanho dos dados. Se houver dados, a função SocketRead() os lê em um buffer, e a quantidade de bytes lida com sucesso é armazenada na variável bytes. Se os dados forem recebidos, o buffer é convertido em string e, em seguida, em um número inteiro (action). Com base no valor de action, a função executa a operação correspondente: se action == 1, uma operação de compra é realizada chamando MBuy(); se action == 2, uma operação de venda é realizada chamando MSell(). Após cada operação, o comando print registra a ação recebida (compra ou venda). Essencialmente, a função escuta comandos de compra ou venda por meio do socket e executa automaticamente as operações correspondentes.

    //+------------------------------------------------------------------+
    //|                        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);
    }

    Função para abrir operações 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);
    }

    Função para abrir operações de venda.


    Script do servidor de socket em Python (servidor da estratégia de negociação)

    Nosso script Python irá carregar o modelo treinado e configurar um servidor via socket para escutar conexões vindas do MQL5. Ao receber os dados, ele fará a previsão e enviará de volta a ação de negociação.

    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()
    

    Salve o script Python como trading-model-server.py ou qualquer outro nome desejado. Abra o terminal ou prompt de comando, navegue até o diretório onde salvou seu modelo e o arquivo trading-model-server.py, e execute o seguinte comando para iniciar a conexão.

    python trading_model_server.py
    


    Considerações finais

    Desenvolvemos um sistema de negociação completo que integra aprendizado de máquina com MQL5 para automatizar a tomada de decisões com base em dados históricos. Começamos com o carregamento e a preparação dos dados históricos do XAUUSD, o cálculo das Bandas de Bollinger e a implementação de outras variáveis-chave, como variação de preços e volumes. Utilizando aprendizado por reforço, mais especificamente o Deep Q-Network (DQN), treinamos o modelo para prever ações de compra e venda com base em padrões nos dados. O modelo treinado foi então conectado ao MQL5 por meio de um sistema de comunicação via sockets, o que permitiu a interação em tempo real entre a plataforma de negociação e nosso modelo de tomada de decisão baseado em Python. Isso nos possibilitou executar operações automaticamente com base nas previsões do modelo, transformando todo o sistema em uma ferramenta poderosa para trading algorítmico.

    Esse tipo de visualização de dados aprimorada e a integração com aprendizado de máquina podem trazer benefícios significativos aos traders, oferecendo percepções mais profundas e permitindo a tomada de decisões mais embasadas. Ao analisar tendências, volatilidade e padrões-chave do mercado, o sistema pode identificar os pontos ideais de entrada e saída para operações. A automatização da execução de ordens com base em modelos orientados por dados reduz o fator humano e o viés emocional, resultando em uma abordagem de negociação mais estratégica e consistente. Essa metodologia fornece aos traders uma ferramenta sofisticada que utiliza dados históricos para melhorar o desempenho, além de economizar tempo ao automatizar tarefas repetitivas de negociação.

    Traduzido do Inglês pela MetaQuotes Ltd.
    Artigo original: https://www.mql5.com/en/articles/16083

    Arquivos anexados |
    Enhanced.ipynb (2101.04 KB)
    EnhancedML.mq5 (3.68 KB)
    XAUUSD_H1_2nd.csv (281.65 KB)
    Últimos Comentários | Ir para discussão (4)
    amrhamed83
    amrhamed83 | 25 out. 2024 em 13:58

    para onde você envia os dados do mt5 para o python?

    Não executei o código, mas parece que ele está faltando.....

    Stanislav Korotky
    Stanislav Korotky | 25 out. 2024 em 22:28
    Não vejo uma "Visualização de dados aprimorada" nesse artigo. O título é enganoso.
    Hlomohang John Borotho
    Hlomohang John Borotho | 28 out. 2024 em 21:46
    amrhamed83 #:

    Para onde você envia os dados do mt5 para o python?

    Não executei o código, mas parece que ele está faltando.....

    Se você se refere a dados para sinais, o funcionamento é que temos um servidor python com o modelo treinado que está conectado ao MetaTrader5, no artigo o servidor python está sendo executado no host local
    HOST = '127.0.0.1'  # Localhost (you can replace this with your IP)
    PORT = 9999         # Port to listen on
    Too Chee Ng
    Too Chee Ng | 18 dez. 2024 em 12:33
    Obrigado por este artigo. Boa estrutura.
    Do básico ao intermediário: Acesso aleatório (I) Do básico ao intermediário: Acesso aleatório (I)
    Neste artigo teremos a nossa primeira experiência no que se refere ao acesso aleatório ao conteúdo de um arquivo. Isto visando tanto a escrita quanto também a leitura de informações e dados presentes em um arquivo. No entanto, como este tema é um tanto quanto longo para ser explicado em um único artigo. Aqui iremos apenas fazer uma introdução sobre esta questão do acesso aleatório.
    MQL5 Trading Toolkit (Parte 3): Desenvolvimento de uma biblioteca EX5 para gerenciamento de ordens pendentes MQL5 Trading Toolkit (Parte 3): Desenvolvimento de uma biblioteca EX5 para gerenciamento de ordens pendentes
    Você aprenderá como desenvolver e implementar uma biblioteca EX5 abrangente para ordens pendentes em seu código ou projetos MQL5. Vamos analisar como importar e implementar essa biblioteca como parte de um painel de negociação ou interface gráfica do usuário (GUI). O painel de ordens do EA permitirá aos usuários abrir, acompanhar e excluir ordens pendentes por número mágico diretamente na interface gráfica exibida na janela do gráfico.
    Simulação de mercado: Position View (V) Simulação de mercado: Position View (V)
    Apesar do que foi visto no artigo anterior, se algo aparentemente simples. Ali, temos diversos problemas e muitas coisas a serem resolvidas e feita. Você caro leitor, pode imaginar que tudo é fácil e simples. E de maneira inocente, vai simplesmente aceitando o que lhe é apresentado. Isto é uma falha, na qual você, caro leitor, deverá tentar se livrar. Mas pior do que aceitar, é simplesmente, não entender e tentar usar algo sem de fato compreender o que está sendo usado. Não é raro, entre iniciantes, a fase de cópia e cola. Porém, caso você não queira ficar sempre nesta, é bom aprender como usar certas ferramentas. E uma das ferramentas mais utilizadas por programadores é a documentação. E a segunda ferramenta é os testes e arquivos de log. Aqui veremos como fazer isto.
    Data Science e ML (Parte 30): O Casal Poderoso para Prever o Mercado de Ações, Redes Neurais Convolucionais (CNNs) e Redes Neurais Recorrentes (RNNs) Data Science e ML (Parte 30): O Casal Poderoso para Prever o Mercado de Ações, Redes Neurais Convolucionais (CNNs) e Redes Neurais Recorrentes (RNNs)
    Neste artigo, exploramos a integração dinâmica das Redes Neurais Convolucionais (CNNs) e das Redes Neurais Recorrentes (RNNs) na previsão do mercado de ações. Aproveitando a capacidade das CNNs de extrair padrões e a proficiência das RNNs em lidar com dados sequenciais. Vamos ver como essa combinação poderosa pode aumentar a precisão e eficiência dos algoritmos de negociação.