
Usando PSAR, Heiken Ashi e Aprendizado Profundo Juntos para Operações de Trading
Introdução
Encontrar uma vantagem pode ser a diferença entre o sucesso e o fracasso. Com tantos dados disponíveis, os traders estão cada vez mais recorrendo a tecnologias avançadas como o aprendizado profundo para obter uma vantagem competitiva. Essa jornada envolve combinar ferramentas tradicionais de análise técnica com modelos modernos de inteligência artificial (IA) para avaliar e se adaptar rapidamente às condições de mercado. Para explorar esse potencial, desenvolvemos um script em Python projetado para unir o poder preditivo do aprendizado profundo com indicadores de trading já consolidados. Este script não é um sistema completo de trading; em vez disso, é uma ferramenta para experimentação rápida — uma maneira ágil de testar se uma estratégia tem potencial de lucratividade. Paralelamente, um script no MetaTrader 5 usa princípios semelhantes para aplicar essa abordagem em ambiente real de trading, fazendo a ponte entre o backtesting teórico e a aplicação prática.
Criando o modelo ONNX com Deep Learning
No mundo acelerado do trading forex, estar à frente da curva é crucial. É por isso que desenvolvemos um script em Python que aproveita o poder do aprendizado profundo para prever movimentos de pares de moedas. Vamos fazer um tour amigável por esse código empolgante e ver como ele funciona!
Nosso script começa importando todas as ferramentas necessárias — pense nisso como reunir os ingredientes para uma receita complexa. Usamos bibliotecas como TensorFlow e Keras, que são potências por trás de muitas aplicações modernas de IA. Também utilizamos o MetaTrader 5 para obter dados reais do mercado de câmbio.
import MetaTrader5 as mt5 import tensorflow as tf import numpy as np import pandas as pd import tf2onnx import keras
Em seguida, configuramos alguns parâmetros importantes. Estamos analisando 120 dias de dados históricos do par EURUSD. Por que 120? É um ponto ideal que nos dá histórico suficiente para identificar padrões sem sobrecarregar nosso modelo.
Então usamos o MetaTrader 5 para buscar esses dados. Imagine como se estivéssemos enviando um viajante do tempo para coletar informações de preço dos últimos 120 dias. Depois que temos esses dados, os organizamos de forma clara para que nossa IA possa compreendê-los.
inp_history_size = 120 sample_size = inp_history_size*3*20 symbol = "EURUSD" optional = "D1_2024" inp_model_name = str(symbol)+"_"+str(optional)+".onnx" if not mt5.initialize(): print("initialize() failed, error code =",mt5.last_error()) quit() # we will save generated onnx-file near the our script to use as resource from sys import argv data_path=argv[0] last_index=data_path.rfind("\\")+1 data_path=data_path[0:last_index] print("data path to save onnx model",data_path) # and save to MQL5\Files folder to use as file terminal_info=mt5.terminal_info() file_path=terminal_info.data_path+"\\MQL5\\Files\\" print("file path to save onnx model",file_path) # set start and end dates for history data from datetime import timedelta, datetime #end_date = datetime.now() end_date = datetime(2024, 1, 1, 0) start_date = end_date - timedelta(days=inp_history_size*20*3) # print start and end dates print("data start date =",start_date) print("data end date =",end_date) # get rates eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)
Dados financeiros brutos podem ser confusos, então fazemos uma limpeza. Focamos nos preços de fechamento — o preço final de cada dia de negociação. Também escalonamos esses dados para ficarem entre 0 e 1. Esse passo é crucial porque ajuda nosso modelo de IA a aprender de forma mais eficaz, como se estivéssemos traduzindo tudo para uma linguagem comum.
from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data)
Não queremos que nosso modelo apenas memorize os dados, então dividimos tudo em duas partes: dados de treino (80%) e dados de teste (20%). É como dar a nosso aluno de IA um livro para estudar (dados de treino) e depois uma prova diferente para testar o que ele aprendeu (dados de teste).
# scale data from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data) # training size is 80% of the data training_size = int(len(scaled_data)*0.80) print("Training_size:",training_size) train_data_initial = scaled_data[0:training_size,:] test_data_initial = scaled_data[training_size:,:1] # split a univariate sequence into samples def split_sequence(sequence, n_steps): X, y = list(), list() for i in range(len(sequence)): # find the end of this pattern end_ix = i + n_steps # check if we are beyond the sequence if end_ix > len(sequence)-1: break # gather input and output parts of the pattern seq_x, seq_y = sequence[i:end_ix], sequence[end_ix] X.append(seq_x) y.append(seq_y) return np.array(X), np.array(y) # split into samples time_step = inp_history_size x_train, y_train = split_sequence(train_data_initial, time_step) x_test, y_test = split_sequence(test_data_initial, time_step) # reshape input to be [samples, time steps, features] which is required for LSTM x_train =x_train.reshape(x_train.shape[0],x_train.shape[1],1) x_test = x_test.reshape(x_test.shape[0],x_test.shape[1],1)
Agora vem a parte empolgante — construir nosso modelo de IA! Estamos usando uma combinação de diferentes técnicas de IA:
- Redes Neurais Convolucionais (CNNs): São ótimas para detectar padrões em sequências, como nossos olhos detectam padrões em imagens.
- Redes LSTM (Long Short-Term Memory): São excelentes para entender dependências de longo prazo nos dados, perfeitas para identificar tendências ao longo do tempo.
- Camadas densas (Dense): Ajudam nosso modelo a fazer as previsões finais.
Também adicionamos algumas camadas de dropout para evitar que o modelo fique confiante demais e acabe "decorando" os dados (overfitting).
# define model from keras.models import Sequential from keras.layers import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM from keras.metrics import RootMeanSquaredError as rmse from tensorflow.keras import callbacks model = Sequential() model.add(Conv1D(filters=256, kernel_size=2, strides=1, padding='same', activation='relu', input_shape=(inp_history_size,1))) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100, return_sequences = True)) model.add(Dropout(0.3)) model.add(LSTM(100, return_sequences = False)) model.add(Dropout(0.3)) model.add(Dense(units=1, activation = 'sigmoid')) model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()]) # Set up early stopping early_stopping = callbacks.EarlyStopping( monitor='val_loss', patience=20, restore_best_weights=True, ) # model training for 300 epochs history = model.fit(x_train, y_train, epochs = 300 , validation_data = (x_test,y_test), batch_size=32, callbacks=[early_stopping], verbose=2)Treinar o modelo é como mandar nossa IA para a escola. Mostramos a ela os dados de treino várias vezes (até 300 épocas), e ela aprende gradualmente a prever os preços futuros com base nos padrões do passado. Usamos algo chamado “early stopping” para garantir que a IA não estude demais e comece a decorar, em vez de aprender.
Depois do treinamento, testamos o desempenho do nosso modelo. Utilizamos métricas como RMSE (Root Mean Square Error) para verificar quão próximas estão nossas previsões dos preços reais. É como corrigir a prova da nossa IA.
# evaluate training data train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = 32) print(f"train_loss={train_loss:.3f}") print(f"train_rmse={train_rmse:.3f}") # evaluate testing data test_loss, test_rmse = model.evaluate(x_test,y_test, batch_size = 32) print(f"test_loss={test_loss:.3f}") print(f"test_rmse={test_rmse:.3f}")
O passo final é converter nosso modelo treinado para o formato ONNX. ONNX é como uma linguagem universal para modelos de IA — permite que o modelo seja utilizado em diferentes ambientes, incluindo nossos scripts para o MetaTrader.
# Define a function that represents your model @tf.function(input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)]) def model_function(x): return model(x) output_path = data_path+inp_model_name # Convert the model to ONNX onnx_model, _ = tf2onnx.convert.from_function( model_function, input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)], opset=13, output_path=output_path ) print(f"Saved ONNX model to {output_path}") # save model to ONNX output_path = data_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") output_path = file_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}")Não nos contentamos apenas com números — criamos vários gráficos para visualizar o desempenho do modelo. Esses gráficos mostram coisas como o progresso de aprendizagem do modelo e como suas previsões se comparam aos preços reais.
Ao final desse script, temos um modelo ONNX poderoso pronto para ser usado em nossas estratégias de trading no forex. Ele foi treinado com dados históricos do EURUSD e pode ajudar a prever movimentos futuros de preço.
Lembre-se: mesmo sendo sofisticado, esse modelo não é uma bola de cristal. O mercado forex é influenciado por muitos fatores complexos. Use previsões de IA como uma ferramenta entre muitas no seu kit de ferramentas de trading — e nunca subestime a importância da gestão de risco.
Script em Python para testar uma estratégia
Este script em Python é basicamente uma forma rápida de testar se uma estratégia de trading tem potencial de ser lucrativa. Não é um sistema completo de negociação, mas sim uma ferramenta para experimentação rápida. Ao combinar um modelo de aprendizado profundo com análise técnica, ele busca identificar uma possível vantagem no mercado sem se prender a otimizações complexas logo de início.
O script começa conectando-se ao MetaTrader 5 para buscar dados históricos de preços do par EUR/USD. Ele usa dados horários para capturar movimentos de curto prazo e aplica velas Heikin Ashi para suavizar o "ruído" do mercado. Isso ajuda a identificar tendências com mais clareza, algo essencial quando se está testando estratégias rapidamente. Em seguida, ele aplica alguns indicadores técnicos bem conhecidos como PSAR, SMA, RSI e ATR. Esses indicadores funcionam como uma checagem rápida das condições do mercado, fornecendo contexto ao modelo para tomar decisões de compra e venda.
import MetaTrader5 as mt5 import pandas as pd import numpy as np import onnxruntime as ort from sklearn.preprocessing import MinMaxScaler from ta.trend import PSARIndicator, SMAIndicator from ta.momentum import RSIIndicator from ta.volatility import AverageTrueRange import matplotlib.pyplot as plt # Inicializar conexión con MetaTrader5 if not mt5.initialize(): print("Inicialización fallida") mt5.shutdown() def get_historical_data(symbol, timeframe, start_date, end_date): rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date) df = pd.DataFrame(rates) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time', inplace=True) return df def calculate_heikin_ashi(df): ha_close = (df['open'] + df['high'] + df['low'] + df['close']) / 4 ha_open = (df['open'].shift(1) + df['close'].shift(1)) / 2 ha_high = df[['high', 'open', 'close']].max(axis=1) ha_low = df[['low', 'open', 'close']].min(axis=1) df['ha_close'] = ha_close df['ha_open'] = ha_open df['ha_high'] = ha_high df['ha_low'] = ha_low return df def add_indicators(df): # Calcular Heikin Ashi df = calculate_heikin_ashi(df) # PSAR con parámetros ajustados psar = PSARIndicator(df['high'], df['low'], df['close'], step=0.02, max_step=0.2) df['psar'] = psar.psar() # Añadir SMA sma = SMAIndicator(df['close'], window=50) df['sma'] = sma.sma_indicator() # Añadir RSI rsi = RSIIndicator(df['close'], window=14) df['rsi'] = rsi.rsi() # Añadir ATR para medir volatilidad atr = AverageTrueRange(df['high'], df['low'], df['close'], window=14) df['atr'] = atr.average_true_range() # Añadir filtro de tendencia simple df['trend'] = np.where(df['close'] > df['sma'], 1, -1) return df
O coração do script está em seu uso de um modelo de aprendizado profundo ONNX pré-treinado. Ao escalonar os dados e inseri-los nesse modelo, o script gera previsões sobre para onde o mercado pode se mover a seguir. Ele combina essas previsões com os indicadores técnicos para criar um conjunto básico de regras de trading. Por exemplo, busca oportunidades de compra quando certas condições se alinham, como o preço estar acima da média móvel e o RSI indicar que o mercado não está sobrecomprado. Por outro lado, sinaliza uma posição de venda quando as condições opostas são atendidas. O script até adiciona mecanismos adaptativos de stop-loss e take-profit para gerenciar o risco, embora sejam mantidos relativamente simples para preservar o foco do script em testes rápidos.
def prepare_data(df, window_size=120): scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(df[['close']]) X = [] for i in range(window_size, len(scaled_data)): X.append(scaled_data[i-window_size:i]) return np.array(X), scaler def load_onnx_model(model_path): return ort.InferenceSession(model_path) def predict_with_onnx(model, input_data): input_name = model.get_inputs()[0].name output_name = model.get_outputs()[0].name return model.run([output_name], {input_name: input_data})[0] def backtest(df, model, scaler, window_size=120, initial_balance=10000): scaled_data = scaler.transform(df[['close']]) predictions = [] for i in range(window_size, len(scaled_data)): X = scaled_data[i-window_size:i].reshape(1, window_size, 1) pred = predict_with_onnx(model, X.astype(np.float32)) predictions.append(scaler.inverse_transform(pred.reshape(-1, 1))[0, 0]) df['predictions'] = [np.nan]*window_size + predictions # Nueva lógica de trading df['position'] = 0 long_condition = ( (df['close'] > df['predictions']) & (df['close'] > df['psar']) & (df['close'] > df['sma']) & (df['rsi'] < 60) & # Condición RSI menos estricta (df['ha_close'] > df['ha_open']) & (df['ha_close'].shift(1) > df['ha_open'].shift(1)) & (df['trend'] == 1) # Solo comprar en tendencia alcista ) short_condition = ( (df['close'] < df['predictions']) & (df['close'] < df['psar']) & (df['close'] < df['sma']) & (df['rsi'] > 40) & # Condición RSI menos estricta (df['ha_close'] < df['ha_open']) & (df['ha_close'].shift(1) < df['ha_open'].shift(1)) & (df['trend'] == -1) # Solo vender en tendencia bajista ) df.loc[long_condition, 'position'] = 1 # Compra df.loc[short_condition, 'position'] = -1 # Venta # Implementar stop-loss y take-profit adaptativos sl_atr_multiple = 2 tp_atr_multiple = 3 for i in range(window_size, len(df)): if df['position'].iloc[i-1] != 0: entry_price = df['close'].iloc[i-1] current_atr = df['atr'].iloc[i-1] if df['position'].iloc[i-1] == 1: # Posición larga sl_price = entry_price - sl_atr_multiple * current_atr tp_price = entry_price + tp_atr_multiple * current_atr if df['low'].iloc[i] < sl_price or df['high'].iloc[i] > tp_price: df.loc[df.index[i], 'position'] = 0 else: # Posición corta sl_price = entry_price + sl_atr_multiple * current_atr tp_price = entry_price - tp_atr_multiple * current_atr if df['high'].iloc[i] > sl_price or df['low'].iloc[i] < tp_price: df.loc[df.index[i], 'position'] = 0 df['returns'] = df['close'].pct_change() df['strategy_returns'] = df['position'].shift(1) * df['returns'] # Calcular balance df['cumulative_returns'] = (1 + df['strategy_returns']).cumprod() df['balance'] = initial_balance * df['cumulative_returns'] return df
Quando se trata de resultados, o script oferece uma visão rápida do desempenho da estratégia. Neste caso, obteve um retorno total de 1,35%, o que significa que transformou US$ 10.000 em US$ 10.135,02 durante o período de teste. Embora esse ganho não seja espetacular, é um resultado positivo, sugerindo que a estratégia tem algum mérito. O Índice de Sharpe, uma medida comum de retorno ajustado ao risco, foi de 0,39. Esse valor indica que, embora tenha havido ganhos, eles vieram acompanhados de um nível razoável de risco. Para um teste rápido, esse índice é uma forma útil de avaliar se a estratégia vale a pena ser explorada mais a fundo.
Retorno total: 1.35% Ratio de Sharpe: 0.39 Balance final: $10135.02
As visualizações geradas pelo script fornecem uma visão útil sobre como a estratégia se comportou. O primeiro gráfico mostra a interação entre o preço de mercado, as previsões do modelo e os sinais de compra e venda. O gráfico de Heiken Ashi oferece uma visão mais limpa das tendências do mercado, ajudando você a ver se a estratégia está capturando os momentos certos de entrada e saída. Por fim, a curva de saldo mostra de forma direta como o saldo da conta mudou ao longo do tempo, destacando tanto os períodos de crescimento quanto os de queda.
Em resumo, este script foi projetado para experimentação rápida. Ele ajuda a avaliar rapidamente se uma estratégia tem potencial, sem mergulhar demais em ajustes finos. Embora mostre que a estratégia pode gerar algum lucro, o Índice de Sharpe modesto sugere que ainda há trabalho a ser feito. Isso o torna um ponto de partida útil — uma maneira rápida de determinar se uma estratégia merece um desenvolvimento mais aprofundado e otimização.
Expert Advisor
Este script para MetaTrader 5 foi projetado para testar rapidamente se uma estratégia de trading pode ser lucrativa, utilizando uma mistura de análise técnica e um modelo de aprendizado profundo. A ideia aqui não é criar um sistema de trading infalível, mas experimentar a combinação de indicadores técnicos tradicionais com o poder do aprendizado de máquina para verificar se existe uma vantagem no mercado.
O script se concentra no par de moedas EUR/USD e utiliza o intervalo de tempo H6 (6 horas). Ele começa configurando alguns indicadores técnicos conhecidos, como o RSI, SMA, PSAR e ATR. Essas são ferramentas comuns que os traders usam para medir o momentum do mercado, a direção da tendência e a volatilidade. Além disso, ele utiliza velas Heiken Ashi, que ajudam a suavizar a ação do preço e facilitam a identificação de tendências ao reduzir o ruído do mercado. Essa configuração inicial estabelece a base para o que o script está tentando alcançar: uma forma sistemática de buscar oportunidades de compra e venda.
int handleRSI, handleSMA, handlePSAR, handleATR; double rsiBuffer[], smaBuffer[], psarBuffer[], atrBuffer[]; double haOpen[], haClose[], haHigh[], haLow[]; CTrade trade;
handleRSI = iRSI(Symbol, Timeframe, RSIPeriod, PRICE_CLOSE); handleSMA = iMA(Symbol, Timeframe, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE); handlePSAR = iSAR(Symbol, Timeframe, PSARStep, PSARMaximum); handleATR = iATR(Symbol, Timeframe, ATRPeriod); if(handleRSI == INVALID_HANDLE || handleSMA == INVALID_HANDLE || handlePSAR == INVALID_HANDLE || handleATR == INVALID_HANDLE) { Print("Error creating indicators"); return(INIT_FAILED); } ArraySetAsSeries(rsiBuffer, true); ArraySetAsSeries(smaBuffer, true); ArraySetAsSeries(psarBuffer, true); ArraySetAsSeries(atrBuffer, true); ArrayResize(haOpen, 3); ArrayResize(haClose, 3); ArrayResize(haHigh, 3); ArrayResize(haLow, 3); ArraySetAsSeries(haOpen, true); ArraySetAsSeries(haClose, true); ArraySetAsSeries(haHigh, true); ArraySetAsSeries(haLow, true);
IndicatorRelease(handleRSI); IndicatorRelease(handleSMA); IndicatorRelease(handlePSAR); IndicatorRelease(handleATR);
O que diferencia esse script é seu uso de um modelo de aprendizado profundo ONNX pré-treinado. Em vez de depender exclusivamente da análise técnica tradicional, o script aproveita o poder preditivo do aprendizado de máquina para prever movimentos de preço. Ele carrega esse modelo diretamente no ambiente de trading, onde recebe dados recentes do mercado, os normaliza e prevê se o preço vai subir, cair ou permanecer o mesmo. Essa previsão é então classificada em uma de três categorias: movimento de alta, movimento de baixa ou movimento insignificante. Ao combinar essas previsões com indicadores técnicos, o script busca tomar decisões de trading mais bem fundamentadas.
#define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; CTrade ExtTrade; int dlsignal=-1; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2 #resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]
//--- create a model from static buffer ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT); if(ExtHandle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (only Close) const long input_shape[] = {1,SAMPLE_SIZE,1}; if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape)) { Print("OnnxSetInputShape error ",GetLastError()); return(INIT_FAILED); }
//--- check new day if(TimeCurrent() >= ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay = TimeCurrent(); ExtNextDay -= ExtNextDay % PeriodSeconds(PERIOD_D1); ExtNextDay += PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent() < ExtNextBar) return; //--- set next bar time ExtNextBar = TimeCurrent(); ExtNextBar -= ExtNextBar % PeriodSeconds(); ExtNextBar += PeriodSeconds(); //--- check min and max float close = (float)iClose(_Symbol, _Period, 0); if(ExtMin > close) ExtMin = close; if(ExtMax < close) ExtMax = close;
void PredictPrice(void) { static vectorf output_data(1); // vector to get result static vectorf x_norm(SAMPLE_SIZE); // vector for prices normalize //--- check for normalization possibility if(ExtMin>=ExtMax) { Print("ExtMin>=ExtMax"); ExtPredictedClass=-1; return; } //--- request last bars if(!x_norm.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,SAMPLE_SIZE)) { Print("CopyRates ",x_norm.Size()); ExtPredictedClass=-1; return; } float last_close=x_norm[SAMPLE_SIZE-1]; //--- normalize prices x_norm-=ExtMin; x_norm/=(ExtMax-ExtMin); //--- run the inference if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,x_norm,output_data)) { Print("OnnxRun"); ExtPredictedClass=-1; return; } //--- denormalize the price from the output value float predicted=output_data[0]*(ExtMax-ExtMin)+ExtMin; //--- classify predicted price movement float delta=last_close-predicted; if(fabs(delta)<=0.00001) ExtPredictedClass=PRICE_SAME; else { if(delta<0) ExtPredictedClass=PRICE_UP; else ExtPredictedClass=PRICE_DOWN; } } //+------------------------------------------------------------------+ //| Gets Min and Max values | //+------------------------------------------------------------------+ void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); }
A lógica de negociação é relativamente simples, mas sofisticada. Para um sinal de compra (posição longa), ele busca o alinhamento de várias condições: o preço atual deve estar acima tanto do PSAR quanto da SMA, o RSI deve estar abaixo de 60 (indicando que o mercado não está sobrecomprado), e as velas Heiken Ashi devem sugerir uma tendência de alta. Crucialmente, o modelo de aprendizado profundo também deve prever um movimento de alta nos preços. Se todos esses fatores estiverem alinhados, o script abre uma posição de compra. Da mesma forma, ele procura condições opostas para abrir uma posição de venda (short). Essa abordagem em camadas visa filtrar ruídos e sinais falsos, garantindo que operações só sejam realizadas quando múltiplos indicadores e o modelo concordarem com a direção.
void CalculateHeikinAshi() { double close[], open[], high[], low[]; ArraySetAsSeries(close, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); int copied = CopyClose(Symbol(), Timeframe, 0, 3, close); copied = MathMin(copied, CopyOpen(Symbol(), Timeframe, 0, 3, open)); copied = MathMin(copied, CopyHigh(Symbol(), Timeframe, 0, 3, high)); copied = MathMin(copied, CopyLow(Symbol(), Timeframe, 0, 3, low)); if(copied < 3) { Print("Not enough data for Heikin Ashi calculation"); return; } // Calculate Heikin Ashi values for the last 3 candles for(int i = 2; i >= 0; i--) { haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4; if(i == 2) { haOpen[i] = (open[i] + close[i]) / 2; } else { haOpen[i] = (haOpen[i+1] + haClose[i+1]) / 2; } haHigh[i] = MathMax(high[i], MathMax(haOpen[i], haClose[i])); haLow[i] = MathMin(low[i], MathMin(haOpen[i], haClose[i])); } // Debug print Print("Heikin Ashi values:"); for(int i = 0; i < 3; i++) { Print("Candle ", i, ": Open=", haOpen[i], " High=", haHigh[i], " Low=", haLow[i], " Close=", haClose[i]); } }
// Copy indicator data if(CopyBuffer(handleRSI, 0, 0, 3, rsiBuffer) <= 0 || CopyBuffer(handleSMA, 0, 0, 3, smaBuffer) <= 0 || CopyBuffer(handlePSAR, 0, 0, 3, psarBuffer) <= 0 || CopyBuffer(handleATR, 0, 0, 3, atrBuffer) <= 0) { Print("Failed to copy indicator data"); return; } CalculateHeikinAshi(); if(ArraySize(haOpen) < 3 || ArraySize(haClose) < 3) { Print("Not enough Heikin Ashi data"); return; } PredictPrice(); int trend = GetTrend(); bool longCondition = close > psarBuffer[1] && close > smaBuffer[1] && rsiBuffer[1] < 60 && haClose[1] > haOpen[1] && haClose[2] > haOpen[2] && ExtPredictedClass == PRICE_UP && trend == 1; bool shortCondition = close < psarBuffer[1] && close < smaBuffer[1] && rsiBuffer[1] > 40 && haClose[1] < haOpen[1] && haClose[2] < haOpen[2] && ExtPredictedClass == PRICE_DOWN && trend == -1; // Debug printing Print("--- Debug Info ---"); Print("Close: ", close, " PSAR: ", psarBuffer[1], " SMA: ", smaBuffer[1]); Print("RSI: ", rsiBuffer[1], " HA Close[1]: ", haClose[1], " HA Open[1]: ", haOpen[1]); Print("HA Close[2]: ", haClose[2], " HA Open[2]: ", haOpen[2]); Print("ExtPredictedClass: ", ExtPredictedClass, " Trend: ", trend); Print("Long Condition: ", longCondition, " Short Condition: ", shortCondition); if(longCondition) { Print("Long Condition Met"); if(PositionsTotal() == 0) { double atrStopLoss = SLATRMultiple * atrBuffer[1]; double minStopLoss = GetMinimumStopLoss(); double sl = close - MathMax(atrStopLoss, minStopLoss); double tp = close + TPATRMultiple * atrBuffer[1]; bool result = trade.Buy(0.01, Symbol(), 0, sl, tp); if(result) Print("Buy order placed successfully. SL: ", sl, " TP: ", tp); else Print("Failed to place buy order. Error: ", GetLastError()); } else { Print("Position already open. Skipping buy order."); } } else if(shortCondition) { Print("Short Condition Met"); if(PositionsTotal() == 0) { double atrStopLoss = SLATRMultiple * atrBuffer[1]; double minStopLoss = GetMinimumStopLoss(); double sl = close + MathMax(atrStopLoss, minStopLoss); double tp = close - TPATRMultiple * atrBuffer[1]; bool result = trade.Sell(0.01, Symbol(), 0, sl, tp); if(result) Print("Sell order placed successfully. SL: ", sl, " TP: ", tp); else Print("Failed to place sell order. Error: ", GetLastError()); } else { Print("Position already open. Skipping sell order."); } } ManageTrailingStop(); }
A gestão de risco também está incorporada no script. Ele usa o Average True Range (ATR) para definir níveis dinâmicos de stop-loss e take-profit. Ao basear esses níveis na volatilidade do mercado, o script se adapta às condições em mudança, com o objetivo de proteger contra perdas significativas e também garantir lucros. Além disso, inclui um mecanismo de trailing stop, que ajusta o stop-loss à medida que a operação se move em uma direção favorável, protegendo ainda mais os ganhos.
double GetMinimumStopLoss() { double spread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) * SymbolInfoDouble(Symbol(), SYMBOL_POINT); double minStopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(Symbol(), SYMBOL_POINT); return MathMax(spread * 2, minStopLevel); // Usamos el doble del spread como mínimo } // Agregar esta función para manejar el trailing stop void ManageTrailingStop() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionSelectByTicket(PositionGetTicket(i))) { if(PositionGetString(POSITION_SYMBOL) == Symbol()) { double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentStopLoss = PositionGetDouble(POSITION_SL); double currentTakeProfit = PositionGetDouble(POSITION_TP); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_BID) - TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits()); if(newStopLoss > currentStopLoss + TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT)) { if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit)) { Print("Trailing stop ajustado para posición larga. Nuevo SL: ", newStopLoss); } } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_ASK) + TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits()); if(newStopLoss < currentStopLoss - TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT)) { if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit)) { Print("Trailing stop ajustado para posición corta. Nuevo SL: ", newStopLoss); } } } } } } }
Em resumo, este script serve como uma maneira rápida e experimental de testar a viabilidade de uma estratégia de trading. Ele não pretende ser a solução perfeita, mas sim um campo de testes para verificar se a combinação de análise técnica e um modelo de aprendizado profundo pode identificar oportunidades lucrativas de trading. Embora não garanta lucros, oferece uma abordagem mais refinada e informada para decisões de negociação, unindo intuição humana com insights de aprendizado de máquina.
Resultados
Os resultados do Expert Advisor (EA) fornecem uma visão valiosa sobre seu desempenho durante o período de backtesting. Começando com um depósito inicial de US$ 10.000, a estratégia obteve um lucro líquido total modesto de 17 unidades, indicando que, embora tenha gerado lucros, estes foram relativamente pequenos em proporção ao investimento inicial. A curva de saldo reflete esse crescimento gradual, mostrando uma trajetória ascendente estável, embora lenta, ao longo do tempo.
Um dos destaques das métricas é o Fator de Lucro (Profit Factor) de 4,39. Esse é um número sólido, sugerindo que, para cada unidade de risco assumida, a estratégia obteve 4,39 unidades de recompensa. Isso implica que o EA foi eficaz em maximizar os lucros em relação às perdas. O Fator de Recuperação (Recovery Factor) de 4,48 reforça ainda mais esse ponto, mostrando que a estratégia foi capaz de se recuperar de períodos de perdas de forma eficiente — um sinal positivo de robustez. No entanto, vale destacar o Índice de Sharpe, que foi de 5,25. Embora esse índice seja relativamente alto e geralmente indique bons retornos ajustados ao risco, o lucro absoluto pequeno sugere que a estratégia talvez tenha assumido riscos muito baixos, resultando em ganhos limitados.
Ao analisar as estatísticas das operações, o EA executou um total de 16 operações, divididas igualmente entre posições longas (compra) e curtas (venda). A taxa de acerto para operações longas foi maior, com 62,5%, enquanto para posições curtas foi de 37,5%. Essa diferença indica que a estratégia foi mais bem-sucedida em capturar movimentos de alta do mercado. Curiosamente, as operações lucrativas representaram exatamente 50% do total, o que mostra que a lucratividade do EA não veio de uma alta taxa de acertos, mas sim da proporção entre ganhos e perdas. As maiores e médias operações lucrativas foram positivas, indicando que o EA conseguiu garantir lucros quando o mercado se moveu a seu favor. A curva de saldo reflete esses resultados, mostrando períodos de perda seguidos por ganhos — evidência de uma abordagem cuidadosa, mas consistente, ao trading.
As métricas de rebaixamento (drawdown) mostram que o EA manteve um nível baixo de exposição ao risco. O drawdown absoluto no saldo foi de 2 unidades, o que é bastante pequeno, e o drawdown relativo foi de apenas 0,02%. Esse drawdown muito baixo sugere que a estratégia do EA era conservadora, priorizando a preservação de capital em vez da busca agressiva por lucros. Essa abordagem conservadora pode ser benéfica em mercados voláteis, mas também ajuda a explicar o lucro líquido relativamente modesto.
Em resumo, o EA demonstrou uma abordagem cautelosa e sistemática ao trading, com foco em manter um alto fator de lucro e reduzir ao máximo os drawdowns. Embora o lucro total tenha sido pequeno, o alto fator de lucro e os baixos rebaixamentos indicam uma estratégia voltada mais para a gestão de riscos do que para a geração rápida de lucros. Isso torna o EA adequado para traders que priorizam crescimento constante e de baixo risco, em vez de estratégias mais agressivas e arriscadas. No entanto, para aqueles que buscam retornos mais elevados, o EA pode precisar de ajustes ou aprimoramentos adicionais para aumentar seu potencial de lucro mantendo sua sólida estrutura de gestão de risco.
Conclusão
A exploração da integração de modelos de aprendizado profundo com análise técnica tradicional mostrou resultados promissores. Por meio do script em Python, vimos como uma abordagem sistemática, usando dados históricos, indicadores-chave e aprendizado de máquina, pode ajudar a identificar oportunidades de trading. Os resultados foram modestos, mas consistentes, demonstrando que tal combinação pode gerar resultados positivos, embora com algumas limitações. Da mesma forma, o Expert Advisor (EA) no MetaTrader 5 confirmou essa abordagem cautelosa. Enquanto o desempenho do EA durante o backtesting revelou um crescimento pequeno, porém estável, com drawdown mínimo, destacou uma estratégia voltada para a gestão de riscos em vez da maximização de lucros. Essa natureza conservadora o torna atrativo para traders que priorizam crescimento estável e de baixo risco. No entanto, para quem busca retornos mais elevados, serão necessárias otimizações e refinamentos adicionais. No fim das contas, essas ferramentas servem como base — um ponto de partida para que traders combinem intuição humana com o poder analítico da IA, oferecendo uma abordagem mais sutil para navegar no complexo mundo do trading forex.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/15868
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