English Русский 中文 Español Deutsch 日本語
preview
Data Science e ML (Parte 30): O Casal Poderoso para Prever o Mercado de Ações, Redes Neurais Convolucionais (CNNs) e Redes Neurais Recorrentes (RNNs)

Data Science e ML (Parte 30): O Casal Poderoso para Prever o Mercado de Ações, Redes Neurais Convolucionais (CNNs) e Redes Neurais Recorrentes (RNNs)

MetaTrader 5Negociação |
138 1
Omega J Msigwa
Omega J Msigwa

Conteúdo:


Introdução

Nos artigos anteriores, vimos o quão poderosas são tanto as Redes Neurais Convolucionais (CNNs) quanto as Redes Neurais Recorrentes (RNNs) e como podem ser empregadas para ajudar a superar o mercado, fornecendo sinais de negociação valiosos.

Neste, vamos tentar combinar duas das técnicas mais poderosas, CNN e RNN, e observar seu impacto preditivo no mercado de ações. Mas, antes disso, vamos entender brevemente do que se tratam CNN e RNN.


Entendendo as Redes Neurais Recorrentes (RNNs) e as Redes Neurais Convolucionais (CNNs)

Redes Neurais Convolucionais (CNNs) são projetadas para reconhecer padrões e características nos dados. Apesar de terem sido originalmente desenvolvidas para tarefas de reconhecimento de imagens, também se saem bem em dados tabulares especificamente preparados para previsão de séries temporais.

Como dito nos artigos anteriores, elas operam aplicando filtros aos dados de entrada e, em seguida, extraem características de alto nível que podem ser úteis para a predição. Em dados de mercado acionário, essas características incluem tendências, efeitos sazonais e anomalias.

rede neural convolucional

Arquitetura de CNN

Aproveitando a natureza hierárquica das CNNs, podemos revelar camadas de representações dos dados, cada uma fornecendo insights sobre diferentes aspectos do mercado.


Redes Neurais Recorrentes (RNNs) são redes neurais artificiais projetadas para reconhecer padrões em sequências de dados, como séries temporais, linguagens ou vídeos.

Ao contrário das redes neurais tradicionais, que assumem que as entradas são independentes entre si, as RNNs conseguem detectar e compreender padrões a partir de uma sequência de dados (informações).

As RNNs são projetadas explicitamente para dados sequenciais. Sua arquitetura lhes permite manter uma memória das entradas anteriores, o que as torna muito adequadas para a previsão de séries temporais, pois são capazes de entender dependências temporais nos dados — algo crucial para fazer previsões precisas no mercado de ações.

Como expliquei na parte 25 desta série de artigos, existem três tipos específicos de RNNs (comumente usados), que incluem a RNN simples (vanilla), a Long Short-Term Memory (LSTM) e a Gated Recurrent Unit (GRU).

Como as CNNs se destacam em extrair e detectar características dos dados, as RNNs são excepcionais em interpretar essas características ao longo do tempo. A ideia é simples: combinar as duas e ver se conseguimos construir um modelo poderoso e robusto, capaz de fazer melhores previsões no mercado de ações.


A sinergia entre CNNs e RNNs

Para integrar as duas, vamos criar os modelos em três etapas.

  1. Extração de características com CNNs
  2. Modelagem temporal com RNNs
  3. Treinamento e Obtenção das Predições

Vamos passar por cada etapa e construir esse modelo robusto composto por RNN e LSTM.


01: Extração de Características com CNNs

Esta primeira etapa envolve alimentar os dados de série temporal em um modelo CNN; o modelo processa os dados, identificando padrões significativos e extraindo características relevantes. 

Usando o conjunto de dados das ações da Tesla, que consiste em valores de Abertura, Máxima, Mínima e Fechamento. Vamos começar preparando os dados em um formato 3D de série temporal, aceitável por CNNs e RNNs.

Vamos criar a variável-alvo para um problema de classificação.

Código Python

target_var = []

open_price = new_df["Open"] 
close_price = new_df["Close"]

for i in range(len(open_price)): 
    if close_price[i] > open_price[i]: # Closing price is greater than opening price
        target_var.append(1) # buy signal
    else:
        target_var.append(0) # sell signal

Normalizamos os dados usando o StandardScaler para torná-los robustos para fins de ML.

X = new_df.iloc[:, :-1]
y = target_var

# Scalling the data

scaler = StandardScaler()
X = scaler.fit_transform(X)

# Train-test split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

print(f"x_train = {X_train.shape} - x_test = {X_test.shape}\n\ny_train = {len(y_train)} - y_test = {len(y_test)}")

 Saída:

x_train = (799, 3) - x_test = (200, 3)

y_train = 799 - y_test = 200

Podemos então preparar os dados em formato de séries temporais.

# creating the sequence 

X_train, y_train = create_sequences(X_train, y_train, time_step)
X_test, y_test = create_sequences(X_test, y_test, time_step)

Como este é um problema de classificação, codificamos em one-hot a variável-alvo.

from tensorflow.keras.utils import to_categorical

y_train_encoded = to_categorical(y_train)
y_test_encoded = to_categorical(y_test)

print(f"One hot encoded\n\ny_train {y_train_encoded.shape}\ny_test {y_test_encoded.shape}")

Saída:

One hot encoded

y_train (794, 2)
y_test (195, 2)

A extração de características é realizada pelo próprio modelo CNN. Vamos fornecer ao modelo os dados brutos que acabamos de preparar.

model = Sequential()
model.add(Conv1D(filters=16, 
                 kernel_size=3, 
                 activation='relu', 
                 strides=2,
                 padding='causal',
                 input_shape=(time_step, X_train.shape[2])
                )
         )

model.add(MaxPooling1D(pool_size=2))


02: Modelagem Temporal com RNNs

As características extraídas na etapa anterior são então passadas para o modelo RNN. O modelo processa essas características considerando a ordem temporal e as dependências presentes nos dados.

Diferentemente da arquitetura de CNN que usamos na parte 27 desta série de artigos, onde utilizamos camadas de rede neural totalmente conectadas (Fully Connected) logo após a camada Flatten. Desta vez, substituímos essas camadas tradicionais de Rede Neural (NN) por camadas de Rede Neural Recorrente (RNN).

Sem esquecer de remover a “camada Flatten” mostrada na imagem da arquitetura de CNN.

Removemos a camada Flatten na arquitetura da CNN porque ela é normalmente usada para converter uma entrada 3D em uma saída 2D, enquanto as RNNs (RNN, LSTM e GRU) esperam uma entrada 3D no formato (tamanho do lote, passos de tempo, características).

model.add(MaxPooling1D(pool_size=2))

model.add(SimpleRNN(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=len(np.unique(y)), activation='softmax'))  # Softmax for binary classification (1 buy, 0 sell signal)


03: Treinamento e Obtenção das Predições

Por fim, podemos prosseguir para treinar o modelo que construímos nas duas etapas anteriores; depois, validamos, medimos seu desempenho e então obtemos as previsões.

Código Python

model.summary()

# Compile the model
optimizer = Adam(learning_rate=0.0001)
model.compile(optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])

# Train the model
early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

history = model.fit(X_train, y_train_encoded, epochs=1000, batch_size=16, validation_split=0.2, callbacks=[early_stopping])

plt.figure(figsize=(7.5, 6))
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.legend()
plt.savefig("training loss curve-rnn-cnn-clf.png")
plt.show()

# Evaluating the Trained Model

y_pred = model.predict(X_test) 


classes_in_y = np.unique(y)
y_pred_binary = classes_in_y[np.argmax(y_pred, axis=1)]

# Confusion Matrix
cm = confusion_matrix(y_test, y_pred_binary)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel("Predicted Label")
plt.ylabel("True Label")
plt.title("Confusion Matrix")
plt.savefig("confusion-matrix RNN + CNN.png")  # Display the heatmap


print("Classification Report\n",
      classification_report(y_test, y_pred_binary))

Saída:

Após avaliar o modelo após 14 épocas, o modelo apresentou 54% de acurácia nos dados de teste.

Curva de perda de treinamento RNN + LSTM

7/7 ━━━━━━━━━━━━━━━━━━━━ 0s 33ms/step
Classification Report
               precision    recall  f1-score   support

           0       0.70      0.40      0.51       117
           1       0.45      0.74      0.56        78

    accuracy                           0.54       195
   macro avg       0.58      0.57      0.54       195
weighted avg       0.60      0.54      0.53       195

Vale mencionar que o treinamento do modelo final levou algum tempo quando mais camadas foram adicionadas; isso se deve à natureza complexa dos dois modelos que combinamos. 

Após o treinamento, precisei salvar o modelo final no formato ONNX.

Código Python

onnx_file_name = "rnn+cnn.TSLA.D1.onnx"

spec = (tf.TensorSpec((None, time_step, X_train.shape[2]), tf.float16, name="input"),)
model.output_names = ['outputs']

onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)

# Save the ONNX model to a file
with open(onnx_file_name, "wb") as f:
    f.write(onnx_model.SerializeToString())

Sem esquecer de salvar também os parâmetros do scaler de padronização.

# Save the mean and scale parameters to binary files

scaler.mean_.tofile(f"{onnx_file_name.replace('.onnx','')}.standard_scaler_mean.bin")
scaler.scale_.tofile(f"{onnx_file_name.replace('.onnx','')}.standard_scaler_scale.bin")

Abri o modelo ONNX salvo no Netron; é enorme.

Modelo CNN + RNN


Semelhante à forma como implantamos a Rede Neural Convolucional (CNN) antes, podemos usar a mesma biblioteca para nos ajudar na tarefa de ler esse modelo massivo com facilidade no MQL5.

#include <MALE5\Convolutional Neural Networks(CNNs)\ConvNet.mqh>
#include <MALE5\preprocessing.mqh>

CConvNet cnn;
StandardizationScaler *scaler; //from preprocessing.mqh

Mas, antes disso, precisamos adicionar o modelo ONNX e os parâmetros do scaler de padronização ao nosso Expert Advisor como recursos.

#resource "\\Files\\rnn+cnn.TSLA.D1.onnx" as uchar onnx_model[]
#resource "\\Files\\rnn+cnn.TSLA.D1.standard_scaler_mean.bin" as double standardization_mean[]
#resource "\\Files\\rnn+cnn.TSLA.D1.standard_scaler_scale.bin" as double standardization_std[]

A primeira coisa que precisamos fazer dentro da função OnInit é inicializar ambos (o scaler de padronização e o modelo de CNN).

int OnInit()
  {
//---
   
   if (!cnn.Init(onnx_model)) //Initialize the Convolutional neural network 
     return INIT_FAILED;
   
   scaler = new StandardizationScaler(standardization_mean, standardization_std); //Initialize the saved scaler by populating it with values 
  
   ...
   ...
        
  return (INIT_SUCCEEDED);       
 }

Para obter as previsões, precisamos normalizar os dados de entrada usando esse scaler pré-carregado; em seguida, aplicamos os dados normalizados ao modelo de CNN e obtemos o sinal previsto e as probabilidades.

   if (NewBar()) //Trade at the opening of a new candle
    {
      CopyRates(Symbol(), PERIOD_D1, 1, time_step, rates);
      
      for (ulong i=0;  i<x_data.Rows(); i++)
        {
          x_data[i][0] = rates[i].open;
          x_data[i][1] = rates[i].high;
          x_data[i][2] = rates[i].low;
        }
   
   //---
            
      x_data = scaler.transform(x_data); //Normalize the data
       
      int signal = cnn.predict_bin(x_data, classes_in_data_); //getting a trading signal from the RNN model
      vector probabilities = cnn.predict_proba(x_data);  //probability for each class
     
      Comment("Probability = ",probabilities,"\nSignal = ",signal);

Abaixo está como o comentário aparece no gráfico.

comentários de sinal e probabilidade no gráfico

O vetor de probabilidades depende das classes presentes na variável-alvo dos seus dados de treinamento. Nos dados de treinamento, preparamos a variável-alvo para indicar 0 para um sinal de venda e 1 para um sinal de compra. Os identificadores ou números das classes devem estar em ordem crescente.

input int time_step = 5; 
input int magic_number = 24092024;
input int slippage = 100;

MqlRates rates[];
matrix x_data(time_step, 3); //3 columns for open, high and low
vector classes_in_data_ = {0, 1}; //unique target variables as they are in the target variable in your training data
int OldNumBars = 0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

A matriz chamada x_data é a responsável pelo armazenamento temporário das variáveis independentes (features) do mercado. Essa matriz é redimensionada para 3 colunas, já que treinamos o modelo com 3 features (Abertura, Máxima e Mínima), e para um número de linhas igual ao valor do passo de tempo (time step).

O valor do passo de tempo deve ser semelhante ao utilizado na criação dos dados de treinamento sequenciais

Podemos montar uma estratégia simples com base nos sinais fornecidos pelo modelo que construímos.

   double min_lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
     
      MqlTick ticks;
      SymbolInfoTick(Symbol(), ticks);
      
      if (signal==1) //if the signal is bullish
       {    
          ClosePos(POSITION_TYPE_SELL); //close sell trades when the signal is buy
          
          if (!PosExists(POSITION_TYPE_BUY)) //There are no buy positions
           {
             if (!m_trade.Buy(min_lot, Symbol(), ticks.ask, 0 , 0)) //Open a buy trade
               printf("Failed to open a buy position err=%d",GetLastError());
           }
       }
      else if (signal==0) //Bearish signal
        {
          ClosePos(POSITION_TYPE_BUY); //close all buy trades when the signal is sell
          
          if (!PosExists(POSITION_TYPE_SELL)) //There are no Sell positions
           {
            if (!m_trade.Sell(min_lot, Symbol(), ticks.bid, 0 , 0)) //open a sell trade
               printf("Failed to open a sell position err=%d",GetLastError());
           }
        }
      else //There was an error
        return;

Agora que temos o modelo carregado e pronto para gerar previsões, executei um teste de 2020.01.01 a 2024.09.01. Abaixo está a imagem com a configuração completa do testador.

Observe que apliquei o EA em um gráfico de 4 horas, em vez do timeframe Diário a partir do qual os dados da ação da Tesla foram coletados. Isso porque programamos a estratégia e os modelos para entrarem em ação no instante em que uma nova vela é aberta, mas a vela diária normalmente abre quando o mercado está fechado, fazendo com que o EA perca operações até o dia seguinte. 

Ao aplicar o EA a um timeframe menor (4 horas, neste caso), garantimos o monitoramento contínuo do mercado a cada 4 horas e a execução de algumas atividades de negociação. 

Isso não afeta os dados fornecidos ao EA, pois aplicamos a função CopyRates no timeframe diário (as decisões de negociação ainda dependem do gráfico diário).

Abaixo está o resultado do Testador.

Impressionante! O EA produziu 90% de trades lucrativos. O modelo de IA era apenas uma RNN simples.

Agora vamos ver como LSTM e GRU se saem no mesmo mercado.


Uma combinação de Rede Neural Convolucional (CNN) e Long Short-Term Memory (LSTM)

Ao contrário da RNN simples, que tem dificuldade em entender padrões em sequências longas de dados ou informações, a LSTM consegue compreender relações e padrões em sequências longas.

As LSTMs costumam ser mais eficientes e precisas do que RNNs simples. Vamos criar um modelo de CNN com LSTM e, em seguida, observar como ele se comporta na ação da Tesla.

Código Python

from tensorflow.keras.layers import LSTM

# Define the CNN model

model = Sequential()
model.add(Conv1D(filters=16, 
                 kernel_size=3, 
                 activation='relu', 
                 strides=2,
                 padding='causal',
                 input_shape=(time_step, X_train.shape[2])
                )
         )

model.add(MaxPooling1D(pool_size=2))


model.add(LSTM(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=len(np.unique(y)), activation='softmax'))  # For binary classification (e.g., buy/sell signal)

model.summary()

Como todas as RNNs podem ser implementadas da mesma forma, precisei fazer apenas uma alteração no bloco de código usado para criar uma RNN simples.

Após treinar e validar o modelo, sua acurácia foi de 53% nos dados de teste.

7/7 ━━━━━━━━━━━━━━━━━━━━ 0s 36ms/step
Classification Report
               precision    recall  f1-score   support

           0       0.67      0.44      0.53       117
           1       0.45      0.68      0.54        78

    accuracy                           0.53       195
   macro avg       0.56      0.56      0.53       195
weighted avg       0.58      0.53      0.53       195

Na linguagem de programação MQL5, podemos usar a mesma biblioteca que utilizamos no EA de RNN simples.

#resource "\\Files\\lstm+cnn.TSLA.D1.onnx" as uchar onnx_model[]
#resource "\\Files\\lstm+cnn.TSLA.D1.standard_scaler_mean.bin" as double standardization_mean[]
#resource "\\Files\\lstm+cnn.TSLA.D1.standard_scaler_scale.bin" as double standardization_std[]

#include <MALE5\Convolutional Neural Networks(CNNs)\ConvNet.mqh>
#include <MALE5\preprocessing.mqh>

CConvNet cnn;
StandardizationScaler *scaler;

O restante do código é mantido igual ao do EA de CNN + RNN.

Usei as mesmas configurações do Testador de antes; abaixo estava o resultado.

Desta vez, a acurácia geral dos trades é de aproximadamente 74%. É menor do que a que obtivemos no modelo anterior, mas ainda assim excelente!


Uma combinação de Rede Neural Convolucional (CNN) e Gated Recurrent Unit (GRU)

Assim como a LSTM, os modelos GRU também são capazes de entender as relações entre longas sequências de informações e dados, apesar de terem uma abordagem mais minimalista em comparação à do modelo LSTM.

Podemos implementá-lo da mesma forma que outros modelos RNN; apenas fazemos a alteração no tipo de modelo no código de construção da arquitetura da CNN.

from tensorflow.keras.layers import GRU

# Define the CNN model

model = Sequential()
model.add(Conv1D(filters=16, 
                 kernel_size=3, 
                 activation='relu', 
                 strides=2,
                 padding='causal',
                 input_shape=(time_step, X_train.shape[2])
                )
         )

model.add(MaxPooling1D(pool_size=2))


model.add(GRU(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(50, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=len(np.unique(y)), activation='softmax'))  # For binary classification (e.g., buy/sell signal)

model.summary()

Após treinar e validar o modelo, ele atingiu uma acurácia semelhante à da LSTM: 53% de acurácia nos dados de teste.

7/7 ━━━━━━━━━━━━━━━━━━━━ 1s 41ms/step
Classification Report
               precision    recall  f1-score   support

           0       0.69      0.39      0.50       117
           1       0.45      0.73      0.55        78

    accuracy                           0.53       195
   macro avg       0.57      0.56      0.53       195
weighted avg       0.59      0.53      0.52       195

Carregamos o modelo GRU em formato ONNX e seus parâmetros do scaler em arquivos binários.

#resource "\\Files\\gru+cnn.TSLA.D1.onnx" as uchar onnx_model[]
#resource "\\Files\\gru+cnn.TSLA.D1.standard_scaler_mean.bin" as double standardization_mean[]
#resource "\\Files\\gru+cnn.TSLA.D1.standard_scaler_scale.bin" as double standardization_std[]

#include <MALE5\Convolutional Neural Networks(CNNs)\ConvNet.mqh>
#include <MALE5\preprocessing.mqh>

CConvNet cnn;
StandardizationScaler *scaler;

Novamente, o restante do código é o mesmo usado no EA de RNN simples.

Depois de testar o modelo no Testador usando as mesmas configurações, abaixo estava o resultado.

O modelo GRU apresentou uma acurácia de aproximadamente 61% — não tão boa quanto a dos dois modelos anteriores, mas ainda assim uma acurácia decente.


Considerações Finais

A integração de Redes Neurais Convolucionais (CNNs) com Redes Neurais Recorrentes (RNNs) pode ser uma abordagem poderosa para previsão no mercado de ações, oferecendo o potencial de revelar padrões ocultos e dependências temporais nos dados. No entanto, essa combinação é relativamente incomum e traz alguns desafios. Um dos principais riscos é o overfitting, especialmente ao aplicar modelos tão sofisticados a problemas relativamente simples. O overfitting pode fazer o modelo ter bom desempenho nos dados de treinamento, mas falhar em generalizar para novos dados.

Além disso, a complexidade de combinar CNNs e RNNs gera custos computacionais significativos, principalmente se você decidir escalar o modelo adicionando mais camadas densas ou aumentando o número de neurônios. É essencial equilibrar cuidadosamente a complexidade do modelo com os recursos disponíveis e o problema em questão.,

Até mais.


Acompanhe o desenvolvimento dos modelos de machine learning e muito mais discutido nesta série de artigos neste repositório do GitHub.

Tabela de Anexos


Nome do Arquivo

Tipo de arquivo Descrição e Uso

Experts\CNN + GRU EA.mq5
Experts\CNN + LSTM EA.mq5
Experts\CNN + RNN EA.mq5

 Expert Advisors Robô de negociação para carregar os modelos ONNX e testar a estratégia de trading no MetaTrader 5.

ConvNet.mqh
preprocessing.mqh

 Arquivos de inclusão


- Este arquivo contém código para carregar modelos de CNN salvos em formato ONNX.
- O scaler de Padronização pode ser encontrado neste arquivo


Files\ *.onnx


 Modelos ONNX


Modelos de machine learning discutidos neste artigo em formato ONNX

Files\*.bin  Arquivos binários
Arquivos binários para carregar os parâmetros do scaler de Padronização para cada modelo


Jupyter Notebook\cnns-rnns.ipynb  

 python/Jupyter notebook   Todo o código Python discutido neste artigo pode ser encontrado dentro deste notebook.

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

Arquivos anexados |
Attachments.zip (342.55 KB)
Últimos Comentários | Ir para discussão (1)
Juan Guirao
Juan Guirao | 13 out. 2024 em 20:30
Esses resultados são surpreendentemente bons. Adicionado à lista TODO.
MQL5 Trading Toolkit (Parte 3): Desenvolvimento de uma biblioteca EX5 para gerenciamento de ordens pendentes MQL5 Trading Toolkit (Parte 3): Desenvolvimento de uma biblioteca EX5 para gerenciamento de ordens pendentes
Você aprenderá como desenvolver e implementar uma biblioteca EX5 abrangente para ordens pendentes em seu código ou projetos MQL5. Vamos analisar como importar e implementar essa biblioteca como parte de um painel de negociação ou interface gráfica do usuário (GUI). O painel de ordens do EA permitirá aos usuários abrir, acompanhar e excluir ordens pendentes por número mágico diretamente na interface gráfica exibida na janela do gráfico.
Criando um Expert Advisor Integrado MQL5-Telegram (Parte 7): Análise de Comandos para Automação de Indicadores em Gráficos Criando um Expert Advisor Integrado MQL5-Telegram (Parte 7): Análise de Comandos para Automação de Indicadores em Gráficos
Neste artigo, exploramos como integrar comandos do Telegram com MQL5 para automatizar a adição de indicadores em gráficos de negociação. Cobrimos o processo de análise (parsing) dos comandos dos usuários, sua execução no MQL5 e o teste do sistema para garantir uma negociação baseada em indicadores de forma fluida.
Integração do MQL5 com pacotes de processamento de dados (Parte 3): Visualização de dados aprimorada Integração do MQL5 com pacotes de processamento de dados (Parte 3): Visualização de dados aprimorada
Neste artigo, vamos explorar a visualização de dados avançada, incluindo recursos como interatividade, dados em camadas e elementos dinâmicos, que permitem aos traders examinar tendências, padrões e correlações com mais eficácia.
Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 9): Expert Advisor de Múltiplas Estratégias (I) Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 9): Expert Advisor de Múltiplas Estratégias (I)
Hoje, vamos explorar as possibilidades de incorporar múltiplas estratégias em um Expert Advisor (EA) usando MQL5. Os Expert Advisors oferecem capacidades mais amplas do que apenas indicadores e scripts, permitindo abordagens de negociação mais sofisticadas que podem se adaptar às mudanças das condições do mercado. Confira mais na discussão deste artigo.