
Desenvolvimento de robô em Python e MQL5 (Parte 1): Pré-processamento de dados
Introdução
O mercado está ficando cada vez mais complexo. Hoje em dia, é praticamente uma batalha de algoritmos. Mais de 95% das negociações são realizadas por robôs.
Portanto, o próximo passo é usar aprendizado de máquina. Não se trata apenas de IA avançada, nem de algoritmos simples. Um modelo de aprendizado de máquina pode lucrar em situações complexas. Aplicar aprendizado de máquina para desenvolver sistemas de trading é uma ideia interessante. Com redes neurais, o robô de trading pode analisar muitos dados, identificar padrões e prever movimentos de preços.
Vamos agora estudar o ciclo de desenvolvimento de um robô de negociação: coletar dados, processá-los, expandir a amostra, engenharia de características, selecionar e treinar o modelo, criar um sistema de trading em Python e monitorar as operações de trading.
Usar Python oferece vantagens, como acelerar o aprendizado de máquina e selecionar e criar características de maneira eficiente. No entanto, exportar modelos para ONNX exige replicar exatamente a mesma lógica de criação de características usada em Python, o que é complicado. Por isso, optamos pelo trading online via Python.
Definição do problema e escolha da ferramenta
O projeto busca criar um modelo de aprendizado de máquina que seja lucrativo e sustentável para trading usando Python. As etapas incluem:
- Coletar dados do MetaTrader 5 e carregar as características iniciais.
- Aumentar os dados para expandir a amostra.
- Rotular os dados.
- Realizar engenharia de características, gerando, agrupando e selecionando-as.
- Selecionar e treinar o modelo de aprendizado de máquina, possivelmente com ensemble.
- Avaliar os modelos com métricas .
- Desenvolver um testador para verificar a lucratividade.
- Alcançar resultados positivos em novos dados.
- Implementar o algoritmo de trading com Python e MQL5.
- Integrar com MetaTrader 5.
- Melhorar os modelos.
Ferramentas: Python MQL5, bibliotecas de ML em Python para rapidez e funcionalidade.
Então, com os objetivos e tarefas definidos, neste artigo serão realizadas as seguintes atividades:
- Coletar dados do MetaTrader 5 e carregar as características iniciais.
- Aumentar os dados para expandir a amostra.
- Rotular os dados.
- Realizar engenharia de características, gerando, agrupando e selecionando-as.
Configuração do ambiente, importações, coleta de dados
Primeiro, é necessário obter dados históricos através do MetaTrader 5. Um código estabelece a conexão com a plataforma de trading, passando o caminho para o arquivo do terminal no método de inicialização.
Em um loop, obtemos os dados com o método mt5.copy_rates_range() com os parâmetros: instrumento, timeframe, datas de início e fim. Aqui existe um contador de tentativas fracassadas e um delay para uma conexão estável.
Uma função encerra a operação, desconectando-a da plataforma com o método mt5.shutdown().
Esta é uma função separada para chamadas futuras no programa.
Para executar o script, você precisará instalar as seguintes bibliotecas:
-
NumPy: Biblioteca para trabalhar com arrays multidimensionais e funções matemáticas.
-
Pandas: Ferramenta para análise de dados, fornece estruturas de dados convenientes para trabalhar com tabelas e séries temporais.
-
Random: Módulo para gerar números aleatórios e selecionar elementos aleatórios de sequências.
-
Datetime: Fornece classes e funções para manipulação de datas e horários.
-
MetaTrader5: Biblioteca para interagir com o terminal de trading MetaTrader 5.
-
Time: Módulo para manipulação de tempo e introdução de atrasos na execução do programa.
-
Concurrent.futures: Ferramenta para execução de tarefas paralelas e assíncronas. Isso será necessário no futuro para trabalho multimoedas paralelo.
-
Tqdm: Biblioteca para exibição de indicadores de progresso durante a execução de operações iterativas. Isso será necessário no futuro para trabalho multimoedas paralelo.
-
Train_test_split: Função para dividir o conjunto de dados em amostras de treino e teste ao treinar modelos de aprendizado de máquina.
Você pode instalá-los usando pip, executando os seguintes comandos: Você pode instalá-los usando pip, executando os seguintes comandos:
pip install numpy pandas MetaTrader5 concurrent-futures tqdm sklearn matplotlib imblearn
import numpy as np import pandas as pd import random from datetime import datetime import MetaTrader5 as mt5 import time import concurrent.futures from tqdm import tqdm from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt from sklearn.utils import class_weight from imblearn.under_sampling import RandomUnderSampler # GLOBALS MARKUP = 0.00001 BACKWARD = datetime(2000, 1, 1) FORWARD = datetime(2010, 1, 1) EXAMWARD = datetime(2024, 1, 1) MAX_OPEN_TRADES = 3 symbol = "EURUSD" def retrieve_data(symbol, retries_limit=300): terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe" attempt = 0 raw_data = None while attempt < retries_limit: if not mt5.initialize(path=terminal_path): print("MetaTrader initialization failed") return None instrument_count = mt5.symbols_total() if instrument_count > 0: print(f"Number of instruments in the terminal: {instrument_count}") else: print("No instruments in the terminal") rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_H1, BACKWARD, EXAMWARD) mt5.shutdown() if rates is None or len(rates) == 0: print(f"Data for {symbol} not available (attempt {attempt+1})") attempt += 1 time.sleep(1) else: raw_data = pd.DataFrame(rates[:-1], columns=['time', 'open', 'high', 'low', 'close', 'tick_volume']) raw_data['time'] = pd.to_datetime(raw_data['time'], unit='s') raw_data.set_index('time', inplace=True) break if raw_data is None: print(f"Data retrieval failed after {retries_limit} attempts") return None # Add simple features raw_data['raw_SMA_10'] = raw_data['close'].rolling(window=10).mean() raw_data['raw_SMA_20'] = raw_data['close'].rolling(window=20).mean() raw_data['Price_Change'] = raw_data['close'].pct_change() * 100 # Additional features raw_data['raw_Std_Dev_Close'] = raw_data['close'].rolling(window=20).std() raw_data['raw_Volume_Change'] = raw_data['tick_volume'].pct_change() * 100 raw_data['raw_Prev_Day_Price_Change'] = raw_data['close'] - raw_data['close'].shift(1) raw_data['raw_Prev_Week_Price_Change'] = raw_data['close'] - raw_data['close'].shift(7) raw_data['raw_Prev_Month_Price_Change'] = raw_data['close'] - raw_data['close'].shift(30) raw_data['Consecutive_Positive_Changes'] = (raw_data['Price_Change'] > 0).astype(int).groupby((raw_data['Price_Change'] > 0).astype(int).diff().ne(0).cumsum()).cumsum() raw_data['Consecutive_Negative_Changes'] = (raw_data['Price_Change'] < 0).astype(int).groupby((raw_data['Price_Change'] < 0).astype(int).diff().ne(0).cumsum()).cumsum() raw_data['Price_Density'] = raw_data['close'].rolling(window=10).apply(lambda x: len(set(x))) raw_data['Fractal_Analysis'] = raw_data['close'].rolling(window=10).apply(lambda x: 1 if x.idxmax() else (-1 if x.idxmin() else 0)) raw_data['Price_Volume_Ratio'] = raw_data['close'] / raw_data['tick_volume'] raw_data['Median_Close_7'] = raw_data['close'].rolling(window=7).median() raw_data['Median_Close_30'] = raw_data['close'].rolling(window=30).median() raw_data['Price_Volatility'] = raw_data['close'].rolling(window=20).std() / raw_data['close'].rolling(window=20).mean() print("\nOriginal columns:") print(raw_data[['close', 'high', 'low', 'open', 'tick_volume']].tail(100)) print("\nList of features:") print(raw_data.columns.tolist()) print("\nLast 100 features:") print(raw_data.tail(100)) # Replace NaN values with the mean raw_data.fillna(raw_data.mean(), inplace=True) return raw_data retrieve_data(symbol)
São definidas variáveis globais: custos de spread e comissões das corretoras, datas para as amostras de treino e teste, número máximo de trades, símbolo.
A cotação é carregada do MetaTrader5 em um loop. Os dados são convertidos em DataFrame e enriquecidos com características:
- Médias móveis SMA de 10 e 20 períodos
- Variação de preço
- Desvio padrão do preço
- Variação de volume
- Variação diária/mensal do preço
- Medianas de preço
Essas características ajudam a considerar fatores que influenciam o preço.
É exibida informação sobre as colunas e os últimos registros do DataFrame.
Vamos verificar como nossa primeira função foi executada:
Tudo funciona. O código carregou as cotações com sucesso, realizou a preparação e o carregamento das características. Vamos para a próxima parte do código.
Aumento de dados para expansão da amostra
Aumentar os dados significa criar novos exemplos de treinamento a partir dos que já existem para ampliar a amostra e melhorar a qualidade do modelo. Isso é especialmente útil na previsão de séries temporais com poucos dados disponíveis. Essa prática ajuda a reduzir o erro dos modelos e a torná-los mais robustos.
Maneiras de aumentar dados financeiros:
- Adicionar ruído (pequenos desvios aleatórios) para tornar o modelo mais resistente a variações.
- Deslocar no tempo para simular diferentes cenários de evolução.
- Escalonar para capturar saltos ou quedas nos preços.
- Inverter os dados brutos.
Criamos uma função que aumenta os dados de entrada, aceitando um DataFrame e o número de novos exemplos para cada método. Ela gera novos dados usando várias abordagens e depois os combina com o DataFrame original.
def augment_data(raw_data, noise_level=0.01, time_shift=1, scale_range=(0.9, 1.1)): print(f"Number of rows before augmentation: {len(raw_data)}") # Copy raw_data into augmented_data augmented_data = raw_data.copy() # Add noise noisy_data = raw_data.copy() noisy_data += np.random.normal(0, noise_level, noisy_data.shape) # Replace NaN values with the mean noisy_data.fillna(noisy_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, noisy_data]) print(f"Added {len(noisy_data)} rows after adding noise") # Time shift shifted_data = raw_data.copy() shifted_data.index += pd.DateOffset(hours=time_shift) # Replace NaN values with the mean shifted_data.fillna(shifted_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, shifted_data]) print(f"Added {len(shifted_data)} rows after time shift") # Scaling scale = np.random.uniform(scale_range[0], scale_range[1]) scaled_data = raw_data.copy() scaled_data *= scale # Replace NaN values with the mean scaled_data.fillna(scaled_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, scaled_data]) print(f"Added {len(scaled_data)} rows after scaling") # Inversion inverted_data = raw_data.copy() inverted_data *= -1 # Replace NaN values with the mean inverted_data.fillna(inverted_data.mean(), inplace=True) augmented_data = pd.concat([augmented_data, inverted_data]) print(f"Added {len(inverted_data)} rows after inversion") print(f"Number of rows after augmentation: {len(augmented_data)}") # Print dates by years print("Print dates by years:") for year, group in augmented_data.groupby(augmented_data.index.year): print(f"Year {year}: {group.index}") return augmented_data
Chamamos o código e obtemos os seguintes resultados:
Após aplicar os métodos de aumento de dados, nosso conjunto inicial de 150.000 barras horárias de preços foi expandido para impressionantes 747.000 linhas. Muitos estudos respeitáveis na área de aprendizado de máquina mostram que esse aumento significativo no volume de dados de treinamento através da geração de exemplos sintéticos tem um efeito positivo nas métricas de qualidade dos modelos treinados. Esperamos que, no nosso caso, essa abordagem também produza o efeito desejado.
Rotulagem de dados
A rotulagem de dados é essencial para o sucesso dos algoritmos de aprendizado supervisionado. Ela permite atribuir rótulos aos dados brutos, que a modelo então aprende. Dados rotulados aumentam a precisão, melhoram a generalização, aceleram o treinamento e facilitam a avaliação dos modelos. Na tarefa de previsão do EURUSD, adicionamos uma coluna binária "labels", indicando se a próxima variação de preço superou o spread e a comissão. Isso permite que a modelo aprenda padrões de superação do spread e tendências sem reversão.
A rotulagem desempenha um papel essencial, permitindo que os algoritmos de aprendizado de máquina identifiquem padrões complexos nos dados, inacessíveis à percepção em sua forma bruta e não processada. Agora, revisemos o código.
def markup_data(data, target_column, label_column, markup_ratio=0.00002): data.loc[:, label_column] = np.where(data.loc[:, target_column].shift(-1) > data.loc[:, target_column] + markup_ratio, 1, 0) data.loc[data[label_column].isna(), label_column] = 0 print(f"Number of markups on price change greater than markup: {data[label_column].sum()}") return data
Executamos este código e obtemos a contagem de rótulos nos dados. A função retorna um DataFrame. Tudo funciona. Aliás, de mais de 700.000 unidades de dados, o preço mudou mais que o spread em apenas 70.000 casos.
Continuamos trabalhando. Agora temos outra função de rotulagem de dados, desta vez mais focada diretamente no lucro.
Função de rótulos-alvo
def label_data(data, symbol, min=2, max=48): terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe" if not mt5.initialize(path=terminal_path): print("Error connecting to MetaTrader 5 terminal") return symbol_info = mt5.symbol_info(symbol) stop_level = 100 * symbol_info.point take_level = 800 * symbol_info.point labels = [] for i in range(data.shape[0] - max): rand = random.randint(min, max) curr_pr = data['close'].iloc[i] future_pr = data['close'].iloc[i + rand] min_pr = data['low'].iloc[i:i + rand].min() max_pr = data['high'].iloc[i:i + rand].max() price_change = abs(future_pr - curr_pr) if price_change > take_level and future_pr > curr_pr and min_pr > curr_pr - stop_level: labels.append(1) # Growth elif price_change > take_level and future_pr < curr_pr and max_pr < curr_pr + stop_level: labels.append(0) # Fall else: labels.append(None) data = data.iloc[:len(labels)].copy() data['labels'] = labels data.dropna(inplace=True) X = data.drop('labels', axis=1) y = data['labels'] rus = RandomUnderSampler(random_state=2) X_balanced, y_balanced = rus.fit_resample(X, y) data_balanced = pd.concat([X_balanced, y_balanced], axis=1) return data
A função obtém os rótulos-alvo para treinar modelos de aprendizado de máquina com base no lucro de trading. Ela se conecta ao MetaTrader 5, extrai informações sobre o símbolo e calcula os níveis de stop/take. Em seguida, para cada ponto de entrada, determina-se o preço futuro em um período aleatório. Se a variação do preço exceder o take e não atingir o stop, e satisfizer as condições de alta/queda, adiciona-se o rótulo 1.0/0.0 respectivamente. Caso contrário, None. É criado um novo DataFrame apenas com os dados rotulados. Os valores None são substituídos pelas médias.
Exibe-se a contagem de rótulos de alta/queda.
Assim, tudo está funcionando. Obtivemos os dados e seus derivados, os dados foram aumentados, substancialmente complementados, rotulados por duas funções diferentes. Vamos para o próximo passo - balanceamento de classes.
Aliás, o leitor experiente em aprendizado de máquina já percebeu há muito tempo que, no final, desenvolveremos um modelo de classificação, não de regressão. Prefiro modelos de regressão, vejo neles um pouco mais de lógica para previsão do que nos modelos de classificação.
Portanto, nosso próximo movimento é balancear classes.
Balanceamento de classes. O que é classificação?
A classificação é um método fundamental de análise, aproveitando a habilidade natural do ser humano de organizar informações com base em características comuns. Um dos primeiros usos da classificação foi na exploração de jazidas, identificando áreas promissoras a partir de características geológicas.
Com os computadores, a classificação atingiu um novo nível, mas a essência permanece, que é descobrir padrões no aparente caos de detalhes. Para os mercados financeiros, é importante classificar a dinâmica dos preços em alta/baixa. No entanto, as classes podem estar desbalanceadas, podendo haver poucas tendências e muitos períodos de consolidação.
Por isso, usamos métodos de balanceamento de classes, como remover exemplos dominantes ou gerar exemplos raros. Isso ajuda os modelos a reconhecer de maneira confiável padrões importantes, mas menos frequentes, na dinâmica dos preços.
pip install imblearn
data = data.iloc[:len(labels)].copy() data['labels'] = labels data.dropna(inplace=True) X = data.drop('labels', axis=1) y = data['labels'] rus = RandomUnderSampler(random_state=2) X_balanced, y_balanced = rus.fit_resample(X, y) data_balanced = pd.concat([X_balanced, y_balanced], axis=1)
Agora, as classes estão balanceadas, temos uma quantidade igual de rótulos para cada classe (queda e alta de preços).
Agora vamos ao que realmente importa na previsão de dados: as características.
O que são características em aprendizado de máquina?
Características e atributos são conceitos simples que aprendemos desde cedo. Quando descrevemos um objeto, mencionamos suas propriedades; essas são as características. Um conjunto dessas características permite que descrevamos um objeto de forma completa.
O mesmo vale para a análise de dados, onde cada observação é descrita por um conjunto de características numéricas e categóricas. Escolher as características certas é crítico para o sucesso.
Em um nível mais elevado, temos a engenharia de características, onde novos atributos derivados são construídos a partir de parâmetros brutos para uma melhor descrição do objeto de estudo.
Dessa forma, a experiência humana de entender o mundo descrevendo objetos com suas características é levada para a ciência, onde é concretizada e automatizada.
Extração automática de características
A engenharia de características é o processo de transformar dados brutos em um conjunto de características para treinar modelos de aprendizado de máquina. O objetivo é identificar as características mais relevantes. Isso pode ser feito manualmente, onde uma pessoa escolhe as características, ou automaticamente, usando algoritmos.
Vamos seguir com a abordagem automática. Vamos usar um método que cria novas características para extrair automaticamente as melhores dos nossos dados. Depois, escolheremos as mais relevantes entre as geradas.
Método de geração de novas características:
def generate_new_features(data, num_features=200, random_seed=1): random.seed(random_seed) new_features = {} for _ in range(num_features): feature_name = f'feature_{len(new_features)}' col1_idx, col2_idx = random.sample(range(len(data.columns)), 2) col1, col2 = data.columns[col1_idx], data.columns[col2_idx] operation = random.choice(['add', 'subtract', 'multiply', 'divide', 'shift', 'rolling_mean', 'rolling_std', 'rolling_max', 'rolling_min', 'rolling_sum']) if operation == 'add': new_features[feature_name] = data[col1] + data[col2] elif operation == 'subtract': new_features[feature_name] = data[col1] - data[col2] elif operation == 'multiply': new_features[feature_name] = data[col1] * data[col2] elif operation == 'divide': new_features[feature_name] = data[col1] / data[col2] elif operation == 'shift': shift = random.randint(1, 10) new_features[feature_name] = data[col1].shift(shift) elif operation == 'rolling_mean': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).mean() elif operation == 'rolling_std': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).std() elif operation == 'rolling_max': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).max() elif operation == 'rolling_min': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).min() elif operation == 'rolling_sum': window = random.randint(2, 20) new_features[feature_name] = data[col1].rolling(window).sum() new_data = pd.concat([data, pd.DataFrame(new_features)], axis=1) print("\nGenerated features:") print(new_data[list(new_features.keys())].tail(100)) return data
Preste atenção a este parâmetro:
random_seed=42
O parâmetro random_seed é necessário para que a geração de novas características possa ser reproduzida. A função generate_new_características cria novas características a partir dos dados brutos. Entrada: dados, número de características, seed.
O gerador aleatório é inicializado com o seed fornecido. No loop: um nome é gerado, 2 características existentes são escolhidas aleatoriamente, e uma operação (adição, subtração, etc.) é selecionada. Uma nova característica é calculada com base na operação escolhida.
Após a geração, as novas características são adicionadas aos dados brutos. Os dados atualizados com as características geradas automaticamente são retornados.
O código permite criar automaticamente novas características para melhorar a qualidade do aprendizado de máquina.
Executamos o código, observamos o resultado:
Foram geradas 100 novas características. Vamos para a próxima etapa, o agrupamento de características.
Agrupamento de características
O agrupamento de características junta características semelhantes em grupos para reduzir a sua quantidade. Isso ajuda a eliminar dados redundantes, reduzir a correlação e simplificar o modelo, evitando o overfitting.
Algoritmos populares: k-means (o número de clusters é definido, as características são agrupadas em torno dos centróides) e agrupamento hierárquica (estrutura hierárquica em forma de árvore).
O agrupamento de características ajuda a lidar com o excesso de características inúteis e melhora a eficiência do modelo.
Vamos analisar o código de agrupamento de características:
from sklearn.mixture import GaussianMixture def cluster_features_by_gmm(data, n_components=4): X = data.drop(['label', 'labels'], axis=1) X = X.replace([np.inf, -np.inf], np.nan) X = X.fillna(X.median()) gmm = GaussianMixture(n_components=n_components, random_state=1) gmm.fit(X) data['cluster'] = gmm.predict(X) print("\nFeature clusters:") print(data[['cluster']].tail(100)) return data
Usamos o algoritmo GMM (Gaussian Mixture Model) para o agrupamento de características. A ideia principal é que os dados são gerados como se fossem uma mistura de distribuições normais, onde cada distribuição é um cluster.
Primeiro, definimos o número de clusters. Em seguida, definimos os parâmetros iniciais do modelo: médias, matrizes de covariância e probabilidades dos clusters. O algoritmo recalcula ciclicamente esses parâmetros pelo método de máxima verossimilhança até que eles parem de mudar.
No final, obtemos os parâmetros finais para cada cluster, que podem ser usados para determinar a qual cluster pertence uma nova característica.
O GMM é uma ferramenta poderosa, aplicada em diversas tarefas. Ela é ideal para dados cujos clusters têm formas complexas e fronteiras difusas.
Este código utiliza o GMM para agrupar características em clusters. Os dados brutos são usados, com as etiquetas de classe removidas. O GMM é aplicado para dividir os dados em um número definido de clusters. Os números dos clusters são adicionados como uma nova coluna. Por fim, a tabela dos clusters gerados é exibida.
Executamos o código de agrupamento e observamos os resultados:
Vamos para a função de seleção das melhores características.
Seleção das melhores características
from sklearn.feature_selection import RFECV from sklearn.ensemble import RandomForestClassifier import pandas as pd def feature_engineering(data, n_features_to_select=10): # Remove the 'label' column as it is not a feature X = data.drop(['label', 'labels'], axis=1) y = data['labels'] # Create a RandomForestClassifier model clf = RandomForestClassifier(n_estimators=100, random_state=1) # Use RFECV to select n_features_to_select best features rfecv = RFECV(estimator=clf, step=1, cv=5, scoring='accuracy', n_jobs=-1, verbose=1, min_features_to_select=n_features_to_select) rfecv.fit(X, y) # Return a DataFrame with the best features, 'label' column, and 'labels' column selected_features = X.columns[rfecv.get_support(indices=True)] selected_data = data[selected_features.tolist() + ['label', 'labels']] # Convert selected_features to a list # Print the table of best features print("\nBest features:") print(pd.DataFrame({'Feature': selected_features})) return selected_data labeled_data_engineered = feature_engineering(labeled_data_clustered, n_features_to_select=10)
Esta função identifica as características mais importantes e úteis (características) nos nossos dados. Os dados brutos com características e etiquetas de classe são inseridos, junto com o número necessário de características a serem selecionadas.
Primeiro, as etiquetas de classe são removidas, pois elas não ajudam na escolha das características. Em seguida, o algoritmo Random Forest é executado — um modelo que constrói uma série de árvores de decisão em conjuntos aleatórios de características.
Após o treinamento de todas as árvores, o Random Forest avalia o quanto cada feature é importante e influencia a classificação. Com base nessas avaliações de importância, a função seleciona as características mais relevantes.
No final, as características selecionadas são adicionadas aos dados, e as etiquetas de classe são reintegradas. A função imprime uma tabela com as características escolhidas e retorna os dados atualizados.
Por que tudo isso? Bem, isso nos permite eliminar as características inúteis que apenas retardam o processo. Mantendo apenas as características mais relevantes, a modelo apresentará resultados melhores ao treinar com esses dados.
Executamos a função e verificamos o resultado:
A melhor característica para previsão de preços foi o próprio preço de abertura. No topo da lista estavam características baseadas em médias móveis, incrementos de preços, desvio padrão, e variações de preço diárias e mensais. As características geradas automaticamente não se mostraram informativas.
O código permite selecionar automaticamente as características importantes, o que pode melhorar o desempenho e a capacidade de generalização da modelo.
Conclusão
Assim, criamos um código de processamento de dados que prepara o terreno para o desenvolvimento do nosso futuro modelo de aprendizado de máquina. Vamos recapitular brevemente os passos que realizamos:
- Os dados brutos do par de moedas euro/dólar foram extraídos da plataforma MetaTrader 5. Em seguida, foram realizadas transformações aplicando operações aleatórias — adição de ruído, deslocamentos, escalonamento — para aumentar significativamente o tamanho da amostra.
- O próximo passo foi a rotulagem dos valores de preço com etiquetas especiais de tendência — alta e baixa, considerando os níveis de stop-loss e take-profit. Para balancear as classes, exemplos redundantes foram removidos.
- Depois, realizamos um procedimento complexo de engenharia de características. Primeiro, centenas de novos atributos derivados foram gerados automaticamente a partir das séries temporais brutas. Em seguida, foi feita o agrupamento usando o método de misturas gaussianas para identificar redundâncias. Finalmente, o algoritmo Random Forest foi utilizado para ranquear e selecionar o subconjunto mais informativo de variáveis.
- Como resultado, formou-se um conjunto de dados enriquecido e de alta qualidade, com características otimizadas. Ele servirá como base para o treinamento futuro de modelos de aprendizado de máquina e o desenvolvimento de uma estratégia de trading em Python com integração ao MetaTrader 5.
No próximo artigo, abordaremos a escolha do modelo de classificação ideal, sua melhoria, implementação de validação cruzada e a criação de uma função de testador para a integração Python/MQL5.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/14350





- 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