
Engenharia de Features com Python e MQL5 (Parte I): Previsão de Médias Móveis para Modelos de IA de Longo Alcance
Ao aplicar IA a qualquer tarefa, devemos nos esforçar para fornecer ao modelo o máximo possível de informações úteis sobre o mundo real. Para descrever diferentes propriedades do mercado aos nossos modelos de IA, precisamos manipular e transformar os dados de entrada — esse processo é chamado de engenharia de features. Esta série de artigos ensinará como transformar seus dados de mercado para reduzir os níveis de erro dos seus modelos. Hoje, vou focar em como usar médias móveis para aumentar o alcance de previsão dos seus modelos de IA de uma forma que ofereça controle completo e uma compreensão razoável da efetividade global da estratégia.
Visão Geral da Estratégia
Na última vez que falamos sobre previsão de médias móveis com IA, apresentei evidências que sugeriam que os valores das médias móveis são mais fáceis para nossos modelos de IA preverem do que os níveis futuros de preço. O link para esse artigo está disponível aqui. No entanto, para termos confiança de que nossas descobertas são significativas, treinei 2 modelos de IA idênticos em mais de 200 símbolos de mercado diferentes e comparei a precisão da previsão de preços com a precisão da previsão da média móvel. Os resultados parecem mostrar que nossos níveis de precisão caem, em média, 34% quando prevemos preços em vez de médias móveis.
Em média, podemos esperar 70% de precisão ao prever as médias móveis, em comparação com uma expectativa de 52% ao prever preços. Todos sabemos que, dependendo do período, o indicador de média móvel não acompanha os níveis de preço muito de perto. Por exemplo, o preço pode cair por 20 velas enquanto as médias móveis sobem no mesmo intervalo. Essa divergência é indesejável, porque é possível prever corretamente a direção futura da média móvel, mas o preço pode divergir. Notavelmente, observamos que a taxa de divergência permanece fixa em torno de 31% em todos os mercados, e nossa capacidade de prever divergências teve média de 68%.
Além disso, a variância da nossa capacidade de prever divergências e da ocorrência de divergência foi de 0.000041 e 0.000386, respectivamente. Isso mostra que nosso modelo é capaz de se corrigir com um nível confiável de habilidade. Membros da comunidade que desejam aplicar IA em estratégias de trading de longo prazo devem considerar essa abordagem alternativa em timeframes mais altos. Nossa discussão está limitada ao M1 por enquanto, porque esse timeframe garante que teremos dados suficientes em todos os 297 mercados para fazermos comparações justas.
Existem muitas possíveis razões pelas quais as médias móveis são mais fáceis de prever do que o próprio preço. Isso pode ser verdade porque prever médias móveis está mais alinhado com a ideia de regressão linear do que prever preços. A regressão linear assume que os dados são uma combinação linear (soma) de várias entradas: as médias móveis são uma soma de valores passados de preço, o que torna a suposição linear verdadeira. Já o preço não é uma simples soma de variáveis do mundo real, mas sim uma relação complexa entre muitas variáveis.
Primeiros Passos
Primeiro, precisaremos importar nossas bibliotecas padrão para computação científica em Python.
#Load the libraries we need import pandas as pd import numpy as np import MetaTrader5 as mt5 from sklearn.model_selection import TimeSeriesSplit,cross_val_score from sklearn.linear_model import LogisticRegression,LinearRegression import matplotlib.pyplot as plt
Vamos inicializar nosso terminal MetaTrader 5.
#Initialize the terminal
mt5.initialize()
Quantos símbolos temos disponíveis?
#The total number of symbols we have print(f"Total Symbols Available: ",mt5.symbols_total())
Obter os nomes de todos os símbolos.
#Get the names of all pairs symbols = mt5.symbols_get() idx = [s.name for s in symbols]
Criar um data frame para armazenar nossos níveis de precisão em todos os símbolos.
global_params = pd.DataFrame(index=idx,columns=["OHLC Error","MAR Error","Noise Levels","Divergence Error"]) global_params
Fig 1: Nosso data frame que armazenará nossos níveis de precisão em todos os mercados do terminal
Definir nosso objeto de divisão de séries temporais.
#Define the time series split object tscv = TimeSeriesSplit(n_splits=5,gap=10)
Medir nossa precisão em todos os símbolos.
#Iterate over all symbols for i in np.arange(global_params.dropna().shape[0],len(idx)): #Fetch M1 Data data = pd.DataFrame(mt5.copy_rates_from_pos(cols[i],mt5.TIMEFRAME_M1,0,50000)) data.rename(columns={"open":"Open","high":"High","low":"Low","close":"Close"},inplace=True) #Define our period period = 10 #Add the classical target data.loc[data["Close"].shift(-period) > data["Close"],"OHLC Target"] = 1 #Calculate the returns data.loc[:,["Open","High","Low","Close"]] = data.loc[:,["Open","High","Low","Close"]].diff(period) data["RMA"] = data["Close"].rolling(period).mean() #Calculate our new target data.dropna(inplace=True) data.reset_index(inplace=True,drop=True) data.loc[data["RMA"].shift(-period) > data["RMA"],"New Target"] = 1 data = data.iloc[0:-period,:] #Calculate the divergence target data.loc[data["OHLC Target"] != data["New Target"],"Divergence Target"] = 1 #Noise ratio global_params.iloc[i,2] = data.loc[data["New Target"] != data["OHLC Target"]].shape[0] / data.shape[0] #Test our accuracy predicting the future close price score = cross_val_score(LogisticRegression(),data.loc[:,["Open","High","Low","Close"]],data["OHLC Target"],cv=tscv) global_params.iloc[i,0] = score.mean() #Test our accuracy predicting the moving average of future returns score = cross_val_score(LogisticRegression(),data.loc[:,["Open","Close","RMA"]],data["New Target"],cv=tscv) global_params.iloc[i,1] = score.mean() #Test our accuracy predicting the future divergence between price and its moving average score = cross_val_score(LogisticRegression(),data.loc[:,["Open","Close","RMA"]],data["Divergence Target"],cv=tscv) global_params.iloc[i,3] = score.mean() print(f"{((i/len(idx)) * 100)}% complete") #We are done print("Done")
Pronto!
Analisando os Resultados
Agora que buscamos nossos dados de mercado e avaliamos nosso modelo nos 2 alvos, vamos resumir os resultados do nosso teste em todos os mercados. Começaremos resumindo nossa precisão ao prever a variação do preço de fechamento futuro. A Fig 2 abaixo mostra o resumo da previsão da variação do preço de fechamento futuro. A linha horizontal vermelha representa o limite de 50% de precisão. Nossa precisão média usando esta técnica é denotada pela linha azul horizontal. Como podemos ver, nossa precisão média não está muito distante do limite de 50%, o que não é uma informação encorajadora.
No entanto, para sermos justos, também podemos observar que alguns mercados em particular estão bem acima da média, podendo ser previstos com mais de 65% de precisão. Isso é impressionante, mas também merece investigação adicional para determinar se os resultados são significativos ou se poderiam ter ocorrido por acaso.
global_params.iloc[:,0].plot() plt.title("OHLC Accuracy") plt.xlabel("Market") plt.ylabel("5-fold Accuracy %") plt.axhline(global_params.iloc[:,0].mean(),linestyle='--') plt.axhline(0.5,linestyle='--',color='red')
Fig 2: Nossa precisão média prevendo preços
Agora vamos voltar nossa atenção para nossa precisão ao prever a variação das médias móveis. A Fig 3 abaixo resume os dados para nós. Novamente, a linha vermelha representa o limite de 50%, a linha dourada representa nossa precisão média ao prever mudanças nos preços e a linha azul é nossa precisão média ao prever mudanças nas médias móveis. Dizer simplesmente que nosso modelo é melhor em prever médias móveis é um eufemismo. Acho que não é mais um assunto para debate, mas simplesmente um fato: nossos modelos são melhores em prever certos indicadores do que em prever preços.
global_params.iloc[:,1].plot() plt.title("Moving Average Returns Accuracy") plt.xlabel("Market") plt.ylabel("5-fold Accuracy %") plt.axhline(global_params.iloc[:,1].mean(),linestyle='--') plt.axhline(global_params.iloc[:,0].mean(),linestyle='--',color='orange') plt.axhline(0.5,linestyle='--',color='red')
Fig 3: Nossa precisão prevendo a variação da média móvel
Agora vamos observar a taxa na qual o preço e a média móvel divergem. Níveis de divergência próximos de 50% são ruins, porque isso significa que não podemos ter certeza razoável se o preço e a média móvel vão se mover juntos ou em direções opostas. Felizmente, os níveis de ruído pareceram constantes em todos os mercados avaliados. Os níveis de ruído variaram entre 35% e 30%.
global_params.iloc[:,2].plot() plt.title("Noise Level") plt.xlabel("Market") plt.ylabel("Percentage of Divergence:Price And Moving Average") plt.axhline(global_params.iloc[:,2].mean(),linestyle='--')
Fig 4: Visualizando nossos níveis de ruído em todos os mercados
Se duas variáveis têm uma razão quase constante, isso pode ser um sinal de que existe uma relação que podemos modelar. Vamos observar quão bem podemos prever divergência entre preço e média móvel. Nosso raciocínio é simples: se nosso modelo prevê que a média móvel cairá, podemos prever de forma razoável se o preço seguirá na mesma direção ou se divergirá dela? Acontece que conseguimos prever divergência com um nível confiável de precisão, quase 70% em média.
global_params.iloc[:,3].plot() plt.title("Divergence Accuracy") plt.xlabel("Market") plt.ylabel("5-fold Accuracy %") plt.axhline(global_params.iloc[:,3].mean(),linestyle='--')
Fig 5: Nossa precisão prevendo divergência entre preço e média móvel
Também podemos resumir nossas descobertas em forma de tabela. Assim, podemos facilmente comparar nossos níveis de precisão entre os retornos do mercado e uma média móvel dos retornos. Note que, mesmo que nossa média móvel possa “atrasar” em relação ao preço, nossa precisão ao prever reversões ainda é significativamente maior do que ao prever o próprio preço.
Métrica | Precisão |
---|---|
Erro de Retornos | 0.525353 |
Erro da Média Móvel dos Retornos | 0.705468 |
Níveis de Ruído | 0.317187 |
Erro de Divergência | 0.682069 |
Vamos ver em quais mercados nosso modelo teve melhor desempenho.
global_params.sort_values("MAR Error",ascending=False)
Fig 6: Nossos mercados de melhor desempenho
Otimização para Nosso Melhor Mercado
Agora vamos projetar e ajustar nosso indicador de média móvel para um dos mercados em que nosso desempenho foi maior. Também faremos uma comparação visual entre nossa nova feature projetada e as features clássicas. Começaremos especificando o mercado selecionado.
symbol = "AUDJPY"
Certifique-se de que conseguimos acessar o terminal.
#Reach the terminal
mt5.initialize()
Agora, buscar os dados de mercado.
data = pd.DataFrame(mt5.copy_rates_from_pos(symbol,mt5.TIMEFRAME_D1,365*2,5000))
Importando as bibliotecas necessárias.
#Standard libraries import seaborn as sns from mpl_toolkits.mplot3d import Axes3D from sklearn.linear_model import LinearRegression from sklearn.neural_network import MLPRegressor from sklearn.metrics import mean_squared_error from sklearn.model_selection import cross_val_score,TimeSeriesSplit
Definir o ponto inicial e final para nosso cálculo de período e nosso horizonte de previsão. Certifique-se de que ambas as entradas sejam da mesma dimensão, caso contrário nosso código irá falhar.
#Define the input range x_min , x_max = 2,100 #Look ahead y_min , y_max = 2,100 #Period
Amostre nosso domínio de entrada em passos de 5 para que nossos cálculos sejam detalhados e pouco custosos de obter.
#Sample input range uniformly x_axis = np.arange(x_min,x_max,2) #Look ahead y_axis = np.arange(y_min,y_max,2) #Period
Crie uma mesh-grid usando nosso x_axis e y_axis. A mesh-grid é composta de 2 matrizes bidimensionais que definem todas as combinações possíveis de períodos e horizontes de previsão que desejamos avaliar.
#Create a meshgrid
x , y = np.meshgrid(x_axis,y_axis)
Em seguida, precisamos de uma função que buscará nossos dados de mercado e os rotulará para avaliação.
def clean_data(look_ahead,period): #Fetch the data from our terminal and clean it up data = pd.DataFrame(mt5.copy_rates_from_pos('AUDJPY',mt5.TIMEFRAME_D1,365*2,5000)) data['time'] = pd.to_datetime(data['time'],unit='s') data['MA'] = data['close'].rolling(period).mean() #Transform the data #Target data['Target'] = data['MA'].shift(-look_ahead) - data['MA'] #Change in price data['close'] = data['close'] - data['close'].shift(period) #Change in MA data['MA'] = data['MA'] - data['MA'].shift(period) data.dropna(inplace=True) data.reset_index(drop=True,inplace=True) return(data)
A função a seguir executará uma validação cruzada de 5 folds em nosso modelo de IA.
#Evaluate the objective function def evaluate(look_ahead,period): #Define the model model = LinearRegression() #Define our time series split tscv = TimeSeriesSplit(n_splits=5,gap=look_ahead) temp = clean_data(look_ahead,period) score = np.mean(cross_val_score(model,temp.loc[:,["Open","High","Low","Close"]],temp["Target"],cv=tscv)) return(score)
Finalmente, nossa função objetivo. Nossa função objetivo é simplesmente o erro de validação em 5 folds do nosso modelo sob as novas configurações que desejamos avaliar. Lembre-se: estamos tentando encontrar a distância ótima no futuro que nosso modelo deve prever e, adicionalmente, localizar o período com o qual devemos calcular as variações no preço.
#Define the objective def objective(x,y): #Define the output matrix results = np.zeros([x.shape[0],y.shape[0]]) #Fill in the output matrix for i in np.arange(0,x.shape[0]): #Select the rows look_ahead = x[i] period = y[i] for j in np.arange(0,y.shape[0]): results[i,j] = evaluate(look_ahead[j],period[j]) return(results)
Avaliaremos a relação do nosso modelo com o mercado ao tentarmos prever diretamente a variação nos níveis de preço. A Fig 7 mostra a relação entre nosso modelo e a variação do preço, enquanto a Fig 8 mostra a relação entre nosso modelo e a variação da média móvel. O ponto branco em ambos os gráficos simboliza a combinação de entradas que resultou no menor erro para nós.
res = objective(x,y) res = np.abs(res)
Plotando o melhor desempenho do nosso modelo ao prever o retorno diário do AUDJPY. Os dados revelam que, ao prevermos as variações futuras do preço, o melhor que conseguimos é prever 1 passo à frente. Traders humanos não olham apenas 1 passo à frente quando fazem suas operações. Portanto, os resultados que obtivemos ao prever retornos de mercado diretamente limitam nossa abordagem e tornam nossos modelos fixados na próxima vela.
plt.contourf(x,y,res,100,cmap="jet") plt.plot(x_axis[res.min(axis=0).argmin()],y_axis[res.min(axis=1).argmin()],'.',color='white') plt.ylabel("Differencing Period") plt.xlabel("Forecast Horizon") plt.title("Linear Regression Accuracy Forecasting AUDJPY Daily Return")
Fig 7: Visualizando a capacidade do nosso modelo em prever níveis futuros de preço
Assim que começamos a prever a variação da média móvel em vez da variação do preço, podemos observar que nosso horizonte de previsão ótimo se desloca para a direita. A Fig 8 abaixo mostra que, ao prevermos a variação das médias móveis, podemos prever de forma confiável 22 passos no futuro, em comparação a apenas 1 passo no futuro ao prevermos a variação do preço.
plt.contourf(x,y,res,100,cmap="jet") plt.plot(x_axis[res.min(axis=0).argmin()],y_axis[res.min(axis=1).argmin()],'.',color='white') plt.ylabel("Differencing Period") plt.xlabel("Forecast Horizon") plt.title("Linear Regression Accuracy Forecasting AUDJPY Daily Moving Average Return")
Fig 8: Visualizando a capacidade do nosso modelo em prever níveis futuros da média móvel
O que é ainda mais impressionante é que, nos pontos ótimos, nossos níveis de erro nos dois alvos foram idênticos. Em outras palavras, é tão fácil para nosso modelo prever a variação da média móvel 40 passos no futuro quanto prever a variação do preço 1 passo no futuro. Portanto, a previsão da média móvel nos dá maior alcance, sem aumentar o erro de nossas previsões.
Ao visualizarmos os resultados do nosso teste em 3D, a diferença entre os 2 alvos fica clara. A Fig 9 abaixo mostra a relação entre as variações nos níveis de preço e os parâmetros de previsão para nosso modelo. Há uma tendência clara nos dados: à medida que tentamos prever mais longe no futuro, nossos resultados pioram. Portanto, quando projetamos nossos modelos de IA dessa forma, eles ficam de certa forma “míopes” e não conseguem prever intervalos maiores que 20 de forma razoável.
A Fig 10 foi criada a partir da relação entre nosso modelo e seu erro ao prever as variações nas médias móveis. O gráfico de superfície mostra propriedades desejáveis. Podemos ver claramente que, à medida que prevemos mais longe no futuro e aumentamos o período de cálculo da variação da média móvel, nossas taxas de erro caem suavemente até um mínimo e depois começam a subir novamente. Essa demonstração visual mostra o quanto é mais fácil para nosso modelo prever a média móvel do que prever o preço.
#Create a surface plot fig , ax = plt.subplots(subplot_kw={"projection":"3d"}) fig.set_size_inches(8,8) ax.plot_surface(x,y,optimal_nn_res,cmap="jet")
Fig 9: Visualizando a relação entre nosso modelo e as variações no preço diário do AUDJPY.
Fig 10: A relação entre nosso modelo e as variações no valor da média móvel no par AUDJPY.
Transformações Não Lineares: Denoising com Wavelet
Até agora, aplicamos apenas transformações lineares aos nossos dados. Podemos explorar ainda mais e aplicar transformações não lineares aos dados de entrada do modelo. A engenharia de features é, às vezes, simplesmente um processo de tentativa e erro. Não temos garantia de obter melhores resultados. Portanto, aplicamos essas transformações de maneira ad-hoc — não temos uma fórmula precisa que nos mostre a “melhor” transformação a aplicar em determinado momento.
A transformação wavelet é uma ferramenta matemática usada para criar uma representação de frequência e tempo dos dados. Ela é comumente usada em tarefas de processamento de sinais e imagens para separar o ruído do sinal que estamos tentando processar. Depois que a transformação é aplicada, nossos dados estarão no domínio da frequência. A ideia é que o ruído em nossos dados venha dos pequenos valores de frequência identificados pela transformação. Todos os valores abaixo de um certo limite são reduzidos a 0 de duas maneiras possíveis. O resultado é uma representação esparsa dos dados originais.
O wavelet denoising tem várias vantagens sobre outras técnicas populares, como a Transformada Rápida de Fourier (FFT). Para leitores que possam não estar familiarizados, a Transformada de Fourier representa qualquer sinal como uma soma de ondas seno e cosseno. Infelizmente, a Transformada de Fourier filtra valores de alta frequência. Isso pode não ser sempre desejável, especialmente para dados onde o sinal está no domínio de alta frequência. Como não sabemos se nosso sinal está no domínio de alta ou baixa frequência, precisamos de uma transformação flexível o suficiente para realizar essa tarefa de forma não supervisionada. A transformada wavelet preservará o sinal nos dados, enquanto filtra o máximo possível do ruído.
Para acompanhar, certifique-se de que você tenha instalado o scikit-learn image e sua dependência PyWavelets. Para leitores que desejam criar um aplicativo completo de trading em MQL5, implementar e depurar a transformação do zero pode ser trabalhoso demais. É mais fácil para nós prosseguir sem ela. E para leitores que desejam interagir com o terminal usando a biblioteca Python, a transformação é uma ferramenta que vale a pena incluir em seu arsenal.
Podemos comparar a mudança na precisão de validação para ver se a transformação está ajudando nosso modelo — e está. Observe: aplicamos a transformação apenas nas entradas do modelo e não no alvo, que é preservado. Observe que nossa precisão de validação de fato caiu. Estamos usando a transformação wavelet com um limiar rígido (hard threshold), que define todos os coeficientes de ruído como 0. Alternativamente, poderíamos usar um limiar suave (soft threshold), que direcionaria os coeficientes de ruído para 0, mas sem necessariamente zerá-los completamente.
#Benchmark Score np.mean(cross_val_score(LinearRegression(),data.loc[:,["MA"]],data["Target"]))
#Wavelet denoising data["Denoised"] = denoise_wavelet( data["MA"], method='BayesShrink', mode='hard', rescale_sigma=True, wavelet_levels = 3, wavelet='sym5' ) np.mean(cross_val_score(LinearRegression(),np.sqrt(np.log(data.loc[:,["Denoised"]])),data["Target"]))
Construindo Modelos de IA Personalizados
Agora que sabemos os parâmetros ideais para até onde devemos prever no futuro, e nosso período ótimo da média móvel. Vamos buscar nossos dados de mercado diretamente do Terminal MetaTrader 5 para garantir que nossos modelos de IA estejam sendo treinados usando os mesmos valores de indicadores que eles observarão durante o trading real. Queremos emular a experiência do trading real o máximo possível.
Nosso período da média móvel, no script, corresponderá ao período ótimo que calculamos acima. Além disso, também buscaremos leituras do RSI em nosso terminal para estabilizar o comportamento do nosso robô de trading com IA. Ao depender das previsões feitas por 2 indicadores separados em vez de apenas 1, nosso modelo de IA pode se tornar mais estável ao longo do tempo.
//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" #property script_show_inputs //+------------------------------------------------------------------+ //| Script Inputs | //+------------------------------------------------------------------+ input int size = 100000; //How much data should we fetch? //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int ma_handler,rsi_handler; double ma_reading[],rsi_reading[]; //+------------------------------------------------------------------+ //| On start function | //+------------------------------------------------------------------+ void OnStart() { //--- Load indicator ma_handler = iMA(Symbol(),PERIOD_CURRENT,40,0,MODE_SMA,PRICE_CLOSE); rsi_handler = iRSI(Symbol(),PERIOD_CURRENT,30,PRICE_CLOSE); //--- Load the indicator values CopyBuffer(ma_handler,0,0,size,ma_reading); CopyBuffer(rsi_handler,0,0,size,rsi_reading); ArraySetAsSeries(ma_reading,true); ArraySetAsSeries(rsi_reading,true); //--- File name string file_name = "Market Data " + Symbol() +" MA RSI " + " As Series.csv"; //--- Write to file int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,","); for(int i= size;i>=0;i--) { if(i == size) { FileWrite(file_handle,"Time","Open","High","Low","Close","MA","RSI"); } else { FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i), iOpen(Symbol(),PERIOD_CURRENT,i), iHigh(Symbol(),PERIOD_CURRENT,i), iLow(Symbol(),PERIOD_CURRENT,i), iClose(Symbol(),PERIOD_CURRENT,i), ma_reading[i], rsi_reading[i] ); } } //--- Close the file FileClose(file_handle); } //+------------------------------------------------------------------+
Agora que criamos nosso script, podemos simplesmente arrastá-lo e soltá-lo no mercado desejado e, em seguida, começar a trabalhar com os dados de mercado. Para que nossos backtests sejam significativos, removemos os últimos 2 anos de dados de mercado do arquivo CSV que geramos. Dessa forma, quando testarmos nossa estratégia de 2023 a 2024, os resultados que observaremos serão um reflexo fiel do desempenho do nosso modelo em dados que ele nunca viu antes.
#Read in the data data = pd.read_csv("Market Data AUDJPY MA RSI As Series.csv") #Let's drop the last two years of data. We'll use that to validate our model in the back test data = data.iloc[365:-(365 * 2),:] data
Fig 11: Treinando nosso modelo com 22 anos de dados de mercado, excluindo o período de 2023-2024.
Agora vamos rotular nossos dados para machine learning. Queremos ajudar nosso modelo a aprender as variações de preço dadas as variações em nossos indicadores técnicos. Para ajudar o modelo a aprender essa relação, transformaremos nossas entradas para denotar o estado atual do indicador. Por exemplo, nosso indicador RSI terá 3 possíveis estados:
- Acima de 70.
- Abaixo de 30.
- Entre 70 e 30.
#MA States data["MA 1"] = 0 data["MA 2"] = 0 data.loc[data["MA"] > data["MA"].shift(40),"MA 1"] = 1 data.loc[data["MA"] <= data["MA"].shift(40),"MA 2"] = 1 #RSI States data["RSI 1"] = 0 data["RSI 2"] = 0 data["RSI 3"] = 0 data.loc[data["RSI"] < 30,"RSI 1"] = 1 data.loc[data["RSI"] > 70,"RSI 2"] = 1 data.loc[(data["RSI"] >= 30) & (data["RSI"] <= 70),"RSI 3"] = 1 #Target data["Target"] = data["Close"].shift(-22) - data["Close"] data["MA Target"] = data["MA"].shift(-22) - data["MA"] #Clean up the data data = data.dropna() data = data.iloc[40:,:] data = data.reset_index(drop=True)
Agora podemos começar a medir nossa precisão.
from sklearn.linear_model import Ridge from sklearn.model_selection import TimeSeriesSplit,cross_val_score
Ao aplicar essas transformações, conseguimos observar a variação média do preço à medida que o RSI passa pelas 3 zonas especificadas. Os coeficientes do nosso modelo linear podem ser interpretados como a variação média de preço associada a cada uma das 3 zonas do RSI. Essas descobertas às vezes podem ir contra os ensinamentos clássicos de como usar o indicador. Por exemplo, nosso modelo Ridge aprendeu que quando a leitura do RSI ultrapassa 70, os preços tendem a cair; caso contrário, quando o RSI está abaixo de 70, os níveis futuros de preço tendem a subir.
#Our model can suggest optimal ways of using the RSI indicator #Our model has learned that on average price tends to fall the RSI reading is less than 30 and increases otherwises model = Ridge() model.fit(data.loc[:,["RSI 1","RSI 2","RSI 3"]] , data["Target"]) model.coef_
Nosso modelo Ridge consegue prever bem os preços futuros apenas considerando o estado atual do RSI.
#RSI state np.mean(cross_val_score(Ridge(),data.loc[:,["RSI 1","RSI 2","RSI 3"]] , data["Target"],cv=tscv))
Da mesma forma, nosso modelo também aprendeu suas próprias regras de trading a partir das variações nos indicadores de média móvel. O primeiro coeficiente do modelo é negativo, o que significa que, quando a média móvel sobe durante 40 velas, ela tende a cair em seguida. E o segundo coeficiente é positivo. Portanto, com base nos dados históricos que buscamos em nosso Terminal, quando a média móvel diária de 40 períodos do AUDJPY cai durante 40 velas, tende a subir logo depois. Nosso modelo aprendeu uma estratégia de reversão à média a partir dos dados.
#Our model can suggest optimal ways of using the RSI indicator #Our model has learned that on average price tends to fall the RSI reading is less than 30 and increases otherwises model = Ridge() model.fit(data.loc[:,["MA 1","MA 2"]] , data["Target"]) model.coef_
Nosso modelo tem desempenho ainda melhor quando fornecemos a ele o estado atual do indicador de média móvel.
#MA state np.mean(cross_val_score(Ridge(),data.loc[:,["MA 1","MA 2"]] , data["Target"],cv=tscv))
Convertendo para ONNX
Agora que encontramos os parâmetros ideais de entrada para nossa previsão de Média Móvel, vamos preparar a conversão do nosso modelo para o formato ONNX. Open Neural Network Exchange (ONNX) nos permite construir modelos de machine learning em uma estrutura independente de linguagem. O protocolo ONNX é uma iniciativa de código aberto para criar uma representação padrão universal para modelos de machine learning, permitindo que possamos construir e implantar modelos em qualquer linguagem que adote totalmente a API do ONNX.
Primeiro, vamos buscar os dados de acordo com as melhores entradas que encontramos.
#Fetch clean data new_data = clean_data(140,130)
Importar as bibliotecas necessárias.
import onnx from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType
Ajustar o modelo RSI em todos os dados disponíveis.
#First we will export the RSI model rsi_model = Ridge() rsi_model.fit(data.loc[:,['RSI 1','RSI 2','RSI 3']],data.loc[:,'Target'])
Ajustar o modelo da média móvel em todos os dados disponíveis.
#Finally we will export the MA model ma_model = Ridge() ma_model.fit(data.loc[:,['MA 1','MA 2']],data.loc[:,'MA Target'])
Definir o formato de entrada do nosso modelo e salvá-lo no disco.
initial_types = [('float_input', FloatTensorType([1, 3]))] onnx.save(convert_sklearn(rsi_model,initial_types=initial_types,target_opset=12),"AUDJPY D1 RSI AI F22 P40.onnx") initial_types = [('float_input', FloatTensorType([1, 2]))] onnx.save(convert_sklearn(ma_model,initial_types=initial_types,target_opset=12),"AUDJPY D1 MA AI F22 P40.onnx")
Implementando em MQL5
Agora estamos prontos para começar a construir nossa aplicação de trading em MQL5. Queremos criar uma aplicação de trading capaz de abrir e fechar posições usando nossos novos insights sobre as médias móveis. Além disso, guiaremos nosso modelo usando indicadores mais lentos, que geram sinais de maneira menos agressiva. Vamos tentar emular a forma como traders humanos não estão sempre forçando uma posição no mercado.
Adicionalmente, também implementaremos trailing stop losses para garantir uma boa gestão de risco. Usaremos o indicador Average True Range (ATR) para definir dinamicamente nossos níveis de stop loss e take profit. Nossa estratégia é baseada principalmente em canais de média móvel.
Nossa estratégia prevê as médias móveis 40 passos à frente para nos dar confirmação antes de abrir qualquer posição. Realizamos um backtest dessa estratégia em 1 ano de dados históricos que não foram mostrados ao modelo durante o treinamento.
Primeiro, começaremos importando o arquivo ONNX que acabamos de criar.
//+------------------------------------------------------------------+ //| GBPUSD AI.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Load our resources | //+------------------------------------------------------------------+ #resource "\\Files\\AUDJPY D1 MA AI F22 P40.onnx" as const uchar onnx_buffer[]; #resource "\\Files\\AUDJPY D1 RSI AI F22 P40.onnx" as const uchar rsi_onnx_buffer[];
Vamos importar as bibliotecas necessárias.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; #include <Trade\OrderInfo.mqh> class COrderInfo;
Agora definiremos as variáveis globais necessárias.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ long onnx_model; int ma_handler,state; double bid,ask,vol; vectorf model_forecast = vectorf::Zeros(1); vectorf rsi_model_output = vectorf::Zeros(1); double min_volume,max_volume_increase, volume_step, buy_stop_loss, sell_stop_loss,atr_stop,risk_equity; double take_profit = 0; double close_price[3],atr_reading[],ma_buffer[]; long min_distance,login; int atr,close_average,ticket_1,ticket_2; bool authorized = false; double margin,lot_step; string currency,server; bool all_closed =true; int rsi_handler; long rsi_onnx_model; double indicator_reading[]; ENUM_ACCOUNT_TRADE_MODE account_type; const double stop_percent = 1;
Definamos nossas variáveis de entrada.
//+------------------------------------------------------------------+ //| Technical indicators | //+------------------------------------------------------------------+ input group "Money Management" input int lot_multiple = 10; // How big should the lot size be? input double profit_target = 0; // Profit Target input double loss_target = 0; // Max Loss Allowed input group "Money Management" const int atr_period = 200; //ATR Period input double atr_multiple =2.5; //ATR Multiple
Agora devemos definir exatamente como nossa aplicação de trading deve ser inicializada. Primeiro, verificaremos se o usuário concedeu permissão para a aplicação operar. Se tivermos permissão para continuar, então carregaremos nossos indicadores técnicos e modelos ONNX.
int OnInit() { //Authorization if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Comment("Press Ctrl + E To Give The Robot Permission To Trade And Reload The Program"); return(INIT_FAILED); } else if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) { Comment("Reload The Program And Make Sure You Clicked Allow Algo Trading"); return(INIT_FAILED); } else { Comment("This License is Genuine"); setup(); } //Everything was okay return(INIT_SUCCEEDED); }
Sempre que nossa aplicação de trading não estiver mais em uso, devemos liberar os recursos que não estamos mais utilizando, para garantir que o usuário final tenha uma boa experiência com a aplicação.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { OnnxRelease(onnx_model); OnnxRelease(rsi_onnx_model); IndicatorRelease(atr) }
Sempre que recebermos novas cotações de preços, atualizaremos nossas variáveis e verificaremos novas oportunidades de trading. Caso contrário, se já tivermos operações abertas, atualizaremos nosso trailing stop loss.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //Update technical data update(); if(PositionsTotal() == 0) { check_setup(); } if(PositionsTotal() > 0) { check_atr_stop(); } }
Para obter uma previsão do nosso modelo, temos que definir os estados atuais do RSI e do indicador de média móvel.
//+------------------------------------------------------------------+ //| Get a prediction from our model | //+------------------------------------------------------------------+ int model_predict(void) { //MA Forecast vectorf model_inputs = vectorf::Zeros(2); vectorf rsi_model_inputs = vectorf::Zeros(3); CopyBuffer(ma_handler,0,0,40,ma_buffer); if(ma_buffer[0] > ma_buffer[39]) { model_inputs[0] = 1; model_inputs[1] = 0; } else if(ma_buffer[0] < ma_buffer[39]) { model_inputs[1] = 1; model_inputs[0] = 0; } //RSI Forecast CopyBuffer(rsi_handler,0,0,1,indicator_reading); if(indicator_reading[0] < 30) { rsi_model_inputs[0] = 1; rsi_model_inputs[1] = 0; rsi_model_inputs[2] = 0; } else if(indicator_reading[0] >70) { rsi_model_inputs[0] = 0; rsi_model_inputs[1] = 1; rsi_model_inputs[2] = 0; } else { rsi_model_inputs[0] = 0; rsi_model_inputs[1] = 0; rsi_model_inputs[2] = 1; } //Model predictions OnnxRun(onnx_model,ONNX_DEFAULT,model_inputs,model_forecast); OnnxRun(rsi_onnx_model,ONNX_DEFAULT,rsi_model_inputs,rsi_model_output); //Evaluate model output for buy setup if(((rsi_model_output[0] > 0) && (model_forecast[0] > 0))) { //AI Models forecast Comment("AI Forecast: UP"); return(1); } //Evaluate model output for a sell setup if((rsi_model_output[0] < 0) && (model_forecast[0] < 0)) { Comment("AI Forecast: DOWN"); return(-1); } //Otherwise no position was found return(0); }
Atualize nossas variáveis globais. É mais limpo realizar essas atualizações em uma única chamada de função, em vez de ter todo o código sendo executado diretamente no manipulador OnTick().
//+------------------------------------------------------------------+ //| Update our market data | //+------------------------------------------------------------------+ void update(void) { ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); buy_stop_loss = 0; sell_stop_loss = 0; static datetime time_stamp; datetime time = iTime(_Symbol,PERIOD_CURRENT,0); check_price(3); CopyBuffer(atr,0,0,1,atr_reading); CopyBuffer(ma_handler,0,0,1,ma_buffer); ArraySetAsSeries(atr_reading,true); atr_stop = ((min_volume + atr_reading[0]) * atr_multiple); //On Every Candle if(time_stamp != time) { //Mark the candle time_stamp = time; OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,min_volume,ask,margin); } }
Carregue os recursos de que precisamos, como nossos indicadores técnicos, informações de conta, informações de mercado e outros dados dessa natureza.
//+------------------------------------------------------------------+ //| Load resources | //+------------------------------------------------------------------+ bool setup(void) { //Account Info currency = AccountInfoString(ACCOUNT_CURRENCY); server = AccountInfoString(ACCOUNT_SERVER); login = AccountInfoInteger(ACCOUNT_LOGIN); //Indicators atr = iATR(_Symbol,PERIOD_CURRENT,atr_period); //Setup technical indicators ma_handler =iMA(Symbol(),PERIOD_CURRENT,40,0,MODE_SMA,PRICE_LOW); vol = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN) * lot_multiple; rsi_handler = iRSI(Symbol(),PERIOD_CURRENT,30,PRICE_CLOSE); //Market Information min_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); max_volume_increase = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX) / SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); min_distance = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); lot_step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); //Define our ONNX model ulong ma_input_shape [] = {1,2}; ulong rsi_input_shape [] = {1,3}; ulong output_shape [] = {1,1}; //Create the model onnx_model = OnnxCreateFromBuffer(onnx_buffer,ONNX_DEFAULT); rsi_onnx_model = OnnxCreateFromBuffer(rsi_onnx_buffer,ONNX_DEFAULT); if((onnx_model == INVALID_HANDLE) || (rsi_onnx_model == INVALID_HANDLE)) { Comment("[ERROR] Failed to load AI module correctly"); return(false); } //Validate I/O if((!OnnxSetInputShape(onnx_model,0,ma_input_shape)) || (!OnnxSetInputShape(rsi_onnx_model,0,rsi_input_shape))) { Comment("[ERROR] Failed to set input shape correctly: ",GetLastError()); return(false); } if((!OnnxSetOutputShape(onnx_model,0,output_shape)) || (!OnnxSetOutputShape(rsi_onnx_model,0,output_shape))) { Comment("[ERROR] Failed to load AI module correctly: ",GetLastError()); return(false); } //Everything went fine return(true); }
Reunindo tudo, é assim que nossa aplicação de trading se parece.
//+------------------------------------------------------------------+ //| GBPUSD AI.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Load our resources | //+------------------------------------------------------------------+ #resource "\\Files\\AUDJPY D1 MA AI F22 P40.onnx" as const uchar onnx_buffer[]; #resource "\\Files\\AUDJPY D1 RSI AI F22 P40.onnx" as const uchar rsi_onnx_buffer[]; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; #include <Trade\OrderInfo.mqh> class COrderInfo; //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ long onnx_model; int ma_handler,state; double bid,ask,vol; vectorf model_forecast = vectorf::Zeros(1); vectorf rsi_model_output = vectorf::Zeros(1); double min_volume,max_volume_increase, volume_step, buy_stop_loss, sell_stop_loss,atr_stop,risk_equity; double take_profit = 0; double close_price[3],atr_reading[],ma_buffer[]; long min_distance,login; int atr,close_average,ticket_1,ticket_2; bool authorized = false; double margin,lot_step; string currency,server; bool all_closed =true; int rsi_handler; long rsi_onnx_model; double indicator_reading[]; ENUM_ACCOUNT_TRADE_MODE account_type; const double stop_percent = 1; //+------------------------------------------------------------------+ //| Technical indicators | //+------------------------------------------------------------------+ input group "Money Management" input int lot_multiple = 10; // How big should the lot size be? input double profit_target = 0; // Profit Target input double loss_target = 0; // Max Loss Allowed input group "Money Management" input int bb_period = 36; //Bollinger band period input int ma_period = 4; //Moving average period const int atr_period = 200; //ATR Period input double atr_multiple =2.5; //ATR Multiple //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //Authorization if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Comment("Press Ctrl + E To Give The Robot Permission To Trade And Reload The Program"); return(INIT_FAILED); } else if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) { Comment("Reload The Program And Make Sure You Clicked Allow Algo Trading"); return(INIT_FAILED); } else { Comment("This License is Genuine"); setup(); } //--- Everything was okay return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- OnnxRelease(onnx_model); OnnxRelease(rsi_onnx_model); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update technical data update(); if(PositionsTotal() == 0) { check_setup(); } if(PositionsTotal() > 0) { check_atr_stop(); } } //+------------------------------------------------------------------+ //| Get a prediction from our model | //+------------------------------------------------------------------+ int model_predict(void) { //MA Forecast vectorf model_inputs = vectorf::Zeros(2); vectorf rsi_model_inputs = vectorf::Zeros(3); CopyBuffer(ma_handler,0,0,40,ma_buffer); if(ma_buffer[0] > ma_buffer[39]) { model_inputs[0] = 1; model_inputs[1] = 0; } else if(ma_buffer[0] < ma_buffer[39]) { model_inputs[1] = 1; model_inputs[0] = 0; } //RSI Forecast CopyBuffer(rsi_handler,0,0,1,indicator_reading); if(indicator_reading[0] < 30) { rsi_model_inputs[0] = 1; rsi_model_inputs[1] = 0; rsi_model_inputs[2] = 0; } else if(indicator_reading[0] >70) { rsi_model_inputs[0] = 0; rsi_model_inputs[1] = 1; rsi_model_inputs[2] = 0; } else { rsi_model_inputs[0] = 0; rsi_model_inputs[1] = 0; rsi_model_inputs[2] = 1; } //Model predictions OnnxRun(onnx_model,ONNX_DEFAULT,model_inputs,model_forecast); OnnxRun(rsi_onnx_model,ONNX_DEFAULT,rsi_model_inputs,rsi_model_output); //Evaluate model output for buy setup if(((rsi_model_output[0] > 0) && (model_forecast[0] > 0))) { //AI Models forecast Comment("AI Forecast: UP"); return(1); } //Evaluate model output for a sell setup if((rsi_model_output[0] < 0) && (model_forecast[0] < 0)) { Comment("AI Forecast: DOWN"); return(-1); } //Otherwise no position was found return(0); } //+------------------------------------------------------------------+ //| Check for valid trade setups | //+------------------------------------------------------------------+ void check_setup(void) { int res = model_predict(); if(res == -1) { Trade.Sell(vol,Symbol(),bid,0,0,"VD V75 AI"); state = -1; } else if(res == 1) { Trade.Buy(vol,Symbol(),ask,0,0,"VD V75 AI"); state = 1; } } //+------------------------------------------------------------------+ //| Update our market data | //+------------------------------------------------------------------+ void update(void) { ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); buy_stop_loss = 0; sell_stop_loss = 0; static datetime time_stamp; datetime time = iTime(_Symbol,PERIOD_CURRENT,0); check_price(3); CopyBuffer(atr,0,0,1,atr_reading); CopyBuffer(ma_handler,0,0,1,ma_buffer); ArraySetAsSeries(atr_reading,true); atr_stop = ((min_volume + atr_reading[0]) * atr_multiple); //On Every Candle if(time_stamp != time) { //Mark the candle time_stamp = time; OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,min_volume,ask,margin); } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Load resources | //+------------------------------------------------------------------+ bool setup(void) { //Account Info currency = AccountInfoString(ACCOUNT_CURRENCY); server = AccountInfoString(ACCOUNT_SERVER); login = AccountInfoInteger(ACCOUNT_LOGIN); //Indicators atr = iATR(_Symbol,PERIOD_CURRENT,atr_period); //Setup technical indicators ma_handler =iMA(Symbol(),PERIOD_CURRENT,40,0,MODE_SMA,PRICE_LOW); vol = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN) * lot_multiple; rsi_handler = iRSI(Symbol(),PERIOD_CURRENT,30,PRICE_CLOSE); //Market Information min_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); max_volume_increase = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX) / SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); min_distance = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); lot_step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); //Define our ONNX model ulong ma_input_shape [] = {1,2}; ulong rsi_input_shape [] = {1,3}; ulong output_shape [] = {1,1}; //Create the model onnx_model = OnnxCreateFromBuffer(onnx_buffer,ONNX_DEFAULT); rsi_onnx_model = OnnxCreateFromBuffer(rsi_onnx_buffer,ONNX_DEFAULT); if((onnx_model == INVALID_HANDLE) || (rsi_onnx_model == INVALID_HANDLE)) { Comment("[ERROR] Failed to load AI module correctly"); return(false); } //--- Validate I/O if((!OnnxSetInputShape(onnx_model,0,ma_input_shape)) || (!OnnxSetInputShape(rsi_onnx_model,0,rsi_input_shape))) { Comment("[ERROR] Failed to set input shape correctly: ",GetLastError()); return(false); } if((!OnnxSetOutputShape(onnx_model,0,output_shape)) || (!OnnxSetOutputShape(rsi_onnx_model,0,output_shape))) { Comment("[ERROR] Failed to load AI module correctly: ",GetLastError()); return(false); } //--- Everything went fine return(true); } //+------------------------------------------------------------------+ //| Close all our open positions | //+------------------------------------------------------------------+ void close_all() { if(PositionsTotal() > 0) { ulong ticket; for(int i =0;i < PositionsTotal();i++) { ticket = PositionGetTicket(i); Trade.PositionClose(ticket); } } } //+------------------------------------------------------------------+ //| Update our trailing ATR stop | //+------------------------------------------------------------------+ void check_atr_stop() { for(int i = PositionsTotal() -1; i >= 0; i--) { string symbol = PositionGetSymbol(i); if(_Symbol == symbol) { ulong ticket = PositionGetInteger(POSITION_TICKET); double position_price = PositionGetDouble(POSITION_PRICE_OPEN); double type = PositionGetInteger(POSITION_TYPE); double current_stop_loss = PositionGetDouble(POSITION_SL); if(type == POSITION_TYPE_BUY) { double atr_stop_loss = (ask - (atr_stop)); double atr_take_profit = (ask + (atr_stop)); if((current_stop_loss < atr_stop_loss) || (current_stop_loss == 0)) { Trade.PositionModify(ticket,atr_stop_loss,atr_take_profit); } } else if(type == POSITION_TYPE_SELL) { double atr_stop_loss = (bid + (atr_stop)); double atr_take_profit = (bid - (atr_stop)); if((current_stop_loss > atr_stop_loss) || (current_stop_loss == 0)) { Trade.PositionModify(ticket,atr_stop_loss,atr_take_profit); } } } } } //+------------------------------------------------------------------+ //| Close our open buy positions | //+------------------------------------------------------------------+ void close_buy() { ulong ticket; int type; if(PositionsTotal() > 0) { for(int i = 0; i < PositionsTotal();i++) { if(PositionGetSymbol(i) == _Symbol) { ticket = PositionGetTicket(i); type = (int)PositionGetInteger(POSITION_TYPE); if(type == POSITION_TYPE_BUY) { Trade.PositionClose(ticket); } } } } } //+------------------------------------------------------------------+ //| Close our open sell positions | //+------------------------------------------------------------------+ void close_sell() { ulong ticket; int type; if(PositionsTotal() > 0) { for(int i = 0; i < PositionsTotal();i++) { if(PositionGetSymbol(i) == _Symbol) { ticket = PositionGetTicket(i); type = (int)PositionGetInteger(POSITION_TYPE); if(type == POSITION_TYPE_SELL) { Trade.PositionClose(ticket); } } } } } //+------------------------------------------------------------------+ //| Get the most recent price values | //+------------------------------------------------------------------+ void check_price(int candles) { for(int i = 0; i < candles;i++) { close_price[i] = iClose(_Symbol,PERIOD_CURRENT,i); } } //+------------------------------------------------------------------+
Agora vamos fazer o backtest do nosso algoritmo de trading usando dados que não foram apresentados ao algoritmo durante o treinamento. O período selecionado vai do início de janeiro de 2023 até 28 de junho de 2024, com cotações diárias no par AUDJPY. Definiremos o parâmetro "Forward" como "No", porque garantimos que as datas selecionadas não foram observadas durante o treinamento do modelo.
Fig 12: O Símbolo e Time Frame que usaremos para avaliar nossa estratégia de trading.
Além disso, vamos simular condições reais de trading definindo primeiro o parâmetro "Delays" como "Random delay". Esse parâmetro controla a latência entre o momento em que nossas ordens são enviadas e quando são executadas. Defini-lo como aleatório é semelhante ao que ocorre no trading real: nossa latência não é constante o tempo todo. Além disso, instruiremos nosso Terminal a modelar o mercado usando real ticks. Essa configuração deixará nosso backtest um pouco mais lento porque o terminal precisará buscar dados detalhados de mercado de nosso corretor pela internet.
Os últimos parâmetros, que controlam o depósito da conta e a alavancagem, devem ser ajustados para se adequar à sua configuração de trading. Assumindo que buscamos com sucesso todos os dados solicitados, nosso backtest começará.
Fig 13: Os parâmetros que usaremos para nosso backtest.
Fig 14: O desempenho da nossa estratégia em dados nos quais o modelo não foi treinado.
Fig 12: Mais detalhes do nosso backtest em dados de mercado não vistos.
Conclusão
Os amplos dados de mercado que analisamos hoje mostram claramente que, se você deseja prever intervalos menores que 40 passos no futuro, provavelmente é melhor prever o preço diretamente. No entanto, se você deseja prever mais de 40 passos no futuro, é mais provável que seja melhor prever a variação da média móvel em vez da variação do preço. Sempre existem mais melhorias esperando para serem observadas e verificarmos a diferença que elas trazem. Podemos ver claramente que qualquer tempo gasto transformando as entradas dos dados vale a pena, pois nos permite expor os relacionamentos subjacentes aos nossos modelos de uma forma mais significativa.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16230
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
O resultado mostrado parece promissor; vou experimentar.
Mais desse tipo, por favor.
Obrigado.
O artigo Trait Engineering with Python and MQL5 (Part I) foi publicado: Modelos de IA para previsão de longo prazo em médias móveis:
Autor: Gamuchirai Zororo Ndawana
Algumas das imagens não estão aparecendo...
Os resultados parecem promissores.
Por favor, mais coisas como essa.
Mais coisas como essa, por favor. Obrigado.
De nada, Too Che Ng.
Definitivamente, ainda há muito mais que pode ser dito, considerando um início tão forte.
Algumas imagens não são exibidas...
Sinto muito em ouvir isso. Tenho certeza de que os moderadores resolverão o problema, pois eles já têm muito o que fazer.