
Integração do MQL5 com pacotes de processamento de dados (Parte 1): Análise avançada de dados e processamento estatístico
Introdução
Os mercados financeiros geram enormes volumes de dados que, por sua vez, podem dificultar a análise técnica. Além disso, a análise técnica realizada manualmente, por si só, não permite que os traders analisem e interpretem padrões, tendências e anomalias nos dados. Um pacote avançado de análise de dados, como o Jupyter Lab, permite que os traders executem análises estatísticas complexas, aprendizado de máquina e visualização de dados. Isso possibilita identificar oportunidades de negociação lucrativas, entender o comportamento do mercado, tendências sazonais e prever movimentos futuros dos preços.
Coleta de Dados Históricos
Para começar, precisamos de dados históricos do MetaTrader 5 no formato .csv. Abra a plataforma MetaTrader, "Serviço" > "Configurações" > aba "Gráficos". Em seguida, selecione a quantidade de barras no gráfico que deseja carregar. É recomendável escolher a opção sem limite de barras, pois trabalharemos com datas e não saberemos quantas barras há em um determinado período de tempo.
Após isso, carregaremos os dados reais. Para isso, vá até "Exibir" > "Símbolos". Você estará na aba "Especificação". Alterne para a aba "Barras" ou "Ticks", dependendo do tipo de dado que deseja carregar. Insira a data inicial e final do período histórico que deseja carregar e, em seguida, clique em "Solicitar" para carregar os dados e salvá-los no formato .csv.
Os dados históricos foram carregados. Agora, precisamos baixar e configurar o ambiente Jupyter Lab para análise. Acesse o site oficial do Jupyter Lab e siga os passos simples para baixá-lo. Dependendo do seu sistema operacional, você terá várias opções de instalação usando pip, conda ou brew.
Carregando dados históricos do MetaTrader 5 no Jupyter Lab
Para carregar corretamente os dados históricos do MetaTrader 5 no Jupyter Lab, você precisa saber a pasta onde os dados foram salvos e, no Jupyter Lab, simplesmente navegar até essa pasta. Primeiro, é necessário carregar os dados e verificar os nomes das colunas. Precisamos conferir os nomes das colunas para processá-los corretamente e evitar erros que possam ocorrer devido a nomes incorretos.
Pré-processamento
Antes de iniciar a análise dos dados, precisamos realizar seu pré-processamento.
1. Análise de data e hora: converter a coluna de data para o formato de data e hora.
2. Processamento de todos os valores ausentes.
3. Criação de novos parâmetros, se necessário.
código Python:
import pandas as pd # Load historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path) # Display the first few rows and column names print(data.head()) print(data.columns)
saída:
Observamos caracteres especiais '<>' dentro das colunas, bem como '\t', indicando que o arquivo está delimitado por tabulação. Agora podemos prosseguir com o carregamento dos dados históricos com o nome correto da coluna.
No código abaixo, faremos o seguinte:
1. Importação de bibliotecas:
- Pandas — poderosa biblioteca para manipulação de dados em Python.
- Para análise técnica dos dados dos mercados financeiros, utilizamos a Technical Analysis Library (TA lib).
2. Carregamento dos dados históricos:
- O caminho do arquivo é usado para especificar a localização do arquivo CSV contendo os dados históricos.
- Pd.read_csv lê o arquivo CSV em um DataFrame do pandas. O delimitador '\t' indica que o arquivo está separado por tabulação.
3. Exibição dos dados:
- Data.head() imprime as primeiras linhas do DataFrame para verificar o conteúdo.
- Data.columns exibe os nomes das colunas para conferir a estrutura do DataFrame.
4. Ordenação dos dados por data:
- Date.sort_values(by='<DATE>', inplace=True) ordena os dados na coluna '<DATE>'. Isso garante que os dados estejam em ordem cronológica.
5. Cálculo do RSI:
- TA.RSI calcula o Índice de Força Relativa (RSI) com base na coluna de preços '<CLOSE>'.
- O período utilizado é 14 períodos.
- Os valores calculados são armazenados em uma nova coluna chamada RSI no DataFrame.
6. Exibição dos dados atualizados:
- Data.head() exibe as primeiras linhas do DataFrame para verificar o cálculo do RSI.
import pandas as pd import talib as ta # Load historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t') # Display the first few rows and column names to verify print(data.head()) print(data.columns) # Ensure data is sorted by the correct date column data.sort_values('<DATE>', inplace=True) # Calculate RSI using the '<CLOSE>' price column data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Display the first few rows to verify print(data.head())
saída:
Observamos que a coluna RSI contém valores NaN, e precisamos processá-los corretamente. O cálculo do indicador RSI exige um número mínimo de pontos de dados. Isso pode resultar em valores NaN para os períodos iniciais. Para tratar os valores NaN, precisamos verificar o tipo de dado da coluna '<CLOSE>'. Certifique-se de acessar corretamente o DataFrame e a coluna correspondente. O código Python a seguir mostra como lidar com os valores NaN.
import pandas as pd import talib as ta # Load the historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t')data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Verify the column names print(data.columns) # Convert the column to the correct data type if necessary data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Calculate the RSI data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Display the RSI values print(data[['<CLOSE>', 'RSI']].tail(20))
Resultado:
Podemos ver que os valores NaN foram tratados corretamente. Se os valores NaN ainda aparecerem nos períodos iniciais (o que é esperado devido ao período de bloqueio (lock-back period)), podemos prosseguir com o processamento da seguinte forma:
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) data['RSI'] = data['RSI'].fillna(0) # or use any other method to handle NaN values
Análise Exploratória de Dados
O principal objetivo da Análise Exploratória de Dados (EDA - Exploratory Data Analysis) é investigar a estrutura subjacente dos dados, resumindo suas principais características. Durante a EDA, identificamos padrões e determinamos tendências e relações nos dados. Também identificamos anomalias e valores atípicos que podem exigir investigação adicional. Além disso, verificamos suposições sobre os dados que podem afetar análises futuras. Na sequência, realizamos a limpeza dos dados, identificando valores ausentes, erros e inconsistências que precisam ser corrigidos.
Utilizamos os seguintes scripts python para realizar a EDA:
import seaborn as sns import matplotlib.pyplot as plt import warnings warnings.filterwarnings("ignore") for i in data.select_dtypes(include="number").columns: sns.histplot(data=data, x=i) plt.show()
В código acima, após importar todas as bibliotecas necessárias, começamos ignorando os avisos para obter uma saída limpa. Em seguida, percorremos as colunas numéricas. 'data.select_dtypes(include="number")' retorna um DataFrame contendo apenas colunas numéricas. 'columns' retorna os nomes dessas colunas. Agora vamos construir histogramas com base nos dados. Abaixo estão os histogramas gerados a partir do código acima.
Após realizar operações estatísticas, podemos prosseguir para o treinamento do modelo com os dados coletados. O objetivo é possibilitar previsões baseadas nos dados históricos coletados. Faremos previsões usando apenas o indicador RSI. Antes de prosseguir, precisamos entender o tipo de relação existente entre os dados. Para isso, utilizamos uma matriz de correlação. Devemos ser capazes de identificar se há uma correlação positiva, negativa ou se não há correlação.
- Correlação positiva: Valores próximos de 1indicam uma correlação positiva forte entre duas variáveis. Por exemplo, se a correlação entre Opene Closefor próxima de 1, isso significa que ambos se movem na mesma direção.
- Correlação negativa: Valores próximos de -1indicam uma forte correlação negativa. Por exemplo, se a correlação entre Volumee Pricefor próxima de -1, significa que, à medida que o volume aumenta, o preço tende a cair.
- Ausência de correlação: valores próximos de 0indicam que não há correlação entre as variáveis.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error import talib as ta # Technical Analysis library # Load the data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t') # Exploratory Data Analysis (EDA) print(data.info()) print(data.describe()) # Visualize the closing price plt.figure(figsize=(12, 6)) plt.plot(data['<CLOSE>']) plt.title('XAUUSD Closing Price') plt.xlabel('<DATE>') plt.ylabel('Price') plt.show() # Feature Engineering data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Drop rows with missing values data.dropna(inplace=True) # Define target variable data['Target'] = data['<CLOSE>'].shift(-1) data.dropna(inplace=True) # Split the data X = data[['RSI']] # Only use RSI as the feature y = data['Target'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False) # Model Development model = RandomForestRegressor(n_estimators=100, random_state=42) model.fit(X_train, y_train) # Predictions y_pred = model.predict(X_test) # Evaluate the model mse = mean_squared_error(y_test, y_pred) print(f'Mean Squared Error: {mse}') # Visualize the predictions plt.figure(figsize=(12, 6)) plt.plot(y_test.index, y_test, label='Actual Values') plt.plot(y_test.index, y_pred, label='Predicted Values') plt.xlabel('Samples') plt.ylabel('TARGET_CLOSE') plt.title('Actual vs Predicted Values') plt.legend() plt.show()
Aqui está o resultado obtido no treinamento do modelo:
A análise dos dados mostra que os valores previstos não correspondem aos valores reais. Isso indica que o modelo não está capturando efetivamente o padrão subjacente. As razões podem ser várias:
1. Poucos dados para treinamento: Se o conjunto de dados for muito pequeno, o modelo pode não ter informações suficientes para aprender adequadamente.
2. Overfitting (Superajuste): O modelo pode ter memorizado os dados de treinamento, em vez de aprender a generalizá-los. Isso ocorre frequentemente quando o modelo é muito complexo ou não está devidamente regularizado.
3. Underfitting (Subajuste): O modelo pode ser muito simples para capturar o padrão nos dados. Isso pode ocorrer se o modelo não possuir características relevantes.
4. Parâmetros inadequados: Escolher parâmetros incorretos ou insuficientes pode fazer com que o modelo não tenha informações adequadas para realizar previsões.
Passos para diagnosticar e corrigir o problema:
1. Verifique os dados: certifique-se de que os dados estão limpos, consistentes e passaram por um bom pré-processamento.
2. Avalie o desempenho do modelo: utilize métricas como o Erro Quadrático Médio (Mean Squared Error, MSE)e o Erro Absoluto Médio (Mean Absolute Error, MAE).
3. Melhore o trabalho com os parâmetros: Experimente diferentes parâmetros, incluindo indicadores técnicos, valores defasados e outras métricas financeiras necessárias.
4. Ajuste os hiperparâmetros: Utilize métodos como Grid Searchou Random Searchpara encontrar os melhores hiperparâmetros para sua modelo.
Juntando tudo no MQL5
Após salvar o modelo, criaremos um script Python para previsão. Esse script carregará o modelo e fará previsões.
import joblib import sys import os
- Joblib - usado para carregar o modelo de aprendizado de máquina serializado.
- Sys - fornece acesso aos argumentos da linha de comando.
- OS - usado para verificar a existência de arquivos e realizar operações com arquivos.
model_path = sys.argv[/home/int_junkie/Documents/ML/random_forest_model.pkl] features_path = sys.argv[/home/int_junkie/Documents/ML/features.txt]
- Sys.argv[1] - primeiro argumento da linha de comando. Esse é o caminho do arquivo do modelo.
- Sys.argv[2] - segundo argumento da linha de comando. Esse é o caminho do arquivo de parâmetros.
print(f"Model path: {model_path}") print(f"Features path: {features_path}")
Exibe informações de depuração. Imprime os caminhos dos arquivos do modelo e dos parâmetros para facilitar o rastreamento de erros.
if not os.path.exists(model_path): print(f"Error: Model file not found at {model_path}") sys.exit(1) if not os.path.exists(features_path): print(f"Error: Features file not found at {features_path}") sys.exit(1)
- Verifica se os arquivos do modelo e dos parâmetros existem.
- Se um deles não for encontrado, o script exibe uma mensagem de erro e termina com código de status 1.
model = joblib.load(model_path)
- Carrega o modelo treinado de aprendizado de máquina a partir do 'model-path'.
with open(features_path, 'r') as f: features = [float(line.strip()) for line in f]
- Abre o arquivo de parâmetros para leitura.
- Lê cada linha, remove espaços extras, converte os valores em número de ponto flutuante e os armazena em uma lista features.
prediction = model.predict([features])[0]
- Usa o modelo carregado para gerar uma previsão com base nos parâmetros carregados.
- Model.predict retorna uma lista de previsões (neste caso, uma única previsão), '[0]' extrai a primeira previsão.
print(prediction)
- Exibe a previsão. Esses dados podem ser usados por outro script ou programa, como o MQL5, para tomar decisões de negociação.
MQL5
Carrega o modelo no OnInit().
int OnInit(){ // Load the model and feature names string modelPath = "/home/int_junkie/Documents/ML/random_forest_model.pkl"; string featurePath = "/home/int_junkie/Documents/ML/features.txt"; // Your code to load the model (use appropriate library for pkl files) // Initialize the features double features[]; int fileHandle = FileOpen(featurePath, FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string line; while(!FileIsEnding(fileHandle)) { line = FileReadString(fileHandle); ArrayResize(features, ArraySize(features) + 1); features[ArraySize(features) - 1] = StringToDouble(line); } FileClose(fileHandle); } return(INIT_SUCCEEDED); }
Lê previsões do script Python no OnTick().
void OnTick(){ // Declare static variables to retain values across function calls static bool isNewBar = false; static int prevBars = 0; // Get the current number of bars int newbar = iBars(_Symbol, _Period); // Check if the number of bars has changed if (prevBars == newbar) { // No new bar isNewBar = false; } else { // New bar detected isNewBar = true; // Update previous bars count to current prevBars = newbar; } // Update the features based on current data double features[]; ArrayResize(features, 1); features[0] = iClose(Symbol(), 0, 0); // Write the features to a file int fileHandle = FileOpen("/home/int_junkie/Documents/ML/features.txt", FILE_WRITE | FILE_TXT); if (fileHandle != INVALID_HANDLE) { for (int i = 0; i < ArraySize(features); i++) { FileWrite(fileHandle, DoubleToString(features[i])); } FileClose(fileHandle); } else { Print("Error: Cannot open features file for writing"); return; } // Call the Python script to get the prediction string command = "python /home/int_junkie/Documents/ML/predict.py /home/int_junkie/Documents/ML/random_forest_model.pkl /home/int_junkie/Documents/ML/features.txt"; int result = ShellExecuteA(command); if(result != 0) { Print("Error: ShellExecuteA failed with code ", result); return; } // Read the prediction from a file Sleep(1000); // Wait for the Python script to complete fileHandle = FileOpen("/home/int_junkie/Documents/ML/prediction.txt", FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string prediction = FileReadString(fileHandle); FileClose(fileHandle); double pred_value = StringToDouble(prediction); // Generate trading signals based on predictions double some_threshold = 0.0; // Define your threshold if (pred_value > some_threshold) { // Buy signal double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),Digits()); double sl = Ask - stopLoss * _Point; double tp = Ask + takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, Ask, sl, tp, "ML"); } else if (pred_value < some_threshold) { // Sell signal double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),Digits()); double sl = Bid + stopLoss * _Point; double tp = Bid - takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, Bid, sl, tp, "ML"); } } else { Print("Error: Cannot open prediction file for reading"); } }
No OnTick, a variável command prepara a string de comando para executar o script Python com os arquivos de parâmetros e modelo como argumentos. ShellExecuteA (command) executa o script Python usando ShellExecuteA. 'Sleep (1000)' espera 1 segundo para o script Python terminar sua execução. Em seguida, abre o arquivo de previsão para leitura. Depois, verifica se o arquivo de previsão foi aberto com sucesso. Se sim, lê a previsão. Se não, exibe um erro. A variável threshold é usada para tomar decisões de negociação. Se seu valor for maior que a previsão, um sinal de compra é gerado. Se for menor, um sinal de venda é gerado.
Considerações finais
Extraímos dados da plataforma de negociação MetaTrader 5. Depois, usamos esses dados no Jupyter Lab para análise e processamento estatístico. Após a análise no Jupyter Lab, integramos o modelo ao MQL5 para tomar decisões de negociação com base nos padrões identificados. A integração do MQL5 com Jupyter Lab resolve a limitação das capacidades analíticas, estatísticas e visuais do MQL5. Esse processo melhora a criação de estratégias, aumenta a eficiência no processamento de dados e proporciona um ambiente colaborativo, flexível e poderoso para análises avançadas e processamento estatístico no trading.
Por fim, graças à integração do MQL5 com o Jupyter Lab, conseguimos realizar análises avançadas de dados e processamento estatístico. Com essas capacidades, podemos desenvolver e otimizar qualquer estratégia de negociação usando métodos avançados. Como resultado, obtemos uma vantagem competitiva significativa no mercado financeiro, que é dinâmico e intensivo em dados.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/15155
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.






- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso