English Русский 中文 Español Deutsch 日本語
preview
Machine Learning e Data Science (Parte 45): Previsão de séries temporais no Forex com o modelo PROPHET do Facebook

Machine Learning e Data Science (Parte 45): Previsão de séries temporais no Forex com o modelo PROPHET do Facebook

MetaTrader 5 — Integração |
23 0
Omega J Msigwa
Omega J Msigwa

Sumário


O que é o modelo Prophet

O Prophet é uma ferramenta de código aberto para previsão de séries temporais, desenvolvida pela Meta (anteriormente Facebook). Seu objetivo é facilitar a geração de previsões precisas para aplicações empresariais e analíticas, especialmente quando os dados apresentam tendências e padrões sazonais bem definidos.

O modelo foi apresentado pelo Facebook (S. J. Taylor e Benjamin Letham, 2018) e, inicialmente, foi desenvolvido para prever dados diários com sazonalidade semanal e anual, considerando também os efeitos dos feriados. Posteriormente, foi ampliado para trabalhar com outros tipos de dados sazonais. Os melhores resultados são obtidos em séries temporais com padrões sazonais bem definidos e um histórico de dados suficientemente extenso.

Conceitos principais:

  • Trend (tendência): representa a direção geral de crescimento ou queda dos dados ao longo de um período prolongado, desconsiderando as oscilações sazonais.
  • Seasonality (sazonalidade): corresponde às oscilações que ocorrem em períodos mais curtos e não caracterizam, por si só, uma tendência.

Neste artigo, vamos analisar e implementar esse modelo com dados do Forex para avaliar como ele pode contribuir para a compreensão dos dados de mercado. Antes disso, porém, vamos examinar suas características em mais detalhes.


Características do modelo Prophet

O Prophet pode ser interpretado como um modelo de regressão não linear, representado pela seguinte equação:

  

Figura 01

em que:

  • representa a tendência linear por partes (ou componente de crescimento);
  • representa os diferentes componentes sazonais;
  • considera os efeitos dos feriados; e representa o erro aleatório, modelado como ruído branco.

1. Componente de tendência

O componente de tendência admite pontos de mudança (changepoints), identificados automaticamente caso não sejam definidos manualmente. Esses pontos correspondem a momentos em que a trajetória da tendência pode mudar, como em movimentos de alta ou queda acentuados.

Também é possível utilizar um modelo de crescimento logístico em vez do linear. Nesse caso, introduz-se um parâmetro de capacidade (cap), que permite modelar efeitos de saturação. Essa abordagem é útil quando o crescimento desacelera à medida que se aproxima de um limite natural.

2. Sazonalidade

No Prophet, a sazonalidade é modelada por meio de séries de Fourier.

Por padrão:

  • A sazonalidade anual utiliza uma série de Fourier de ordem 10.
  • A sazonalidade semanal utiliza uma série de Fourier de ordem 3.

Esses termos de Fourier permitem que o modelo identifique padrões sazonais recorrentes.

3. Efeitos dos feriados

Os efeitos dos feriados são incorporados ao modelo por meio de variáveis indicadoras (one-hot encoding), permitindo ajustar as previsões em torno de datas específicas que, historicamente, provocaram alterações no comportamento dos dados. Entre os exemplos estão a divulgação de notícias econômicas e os feriados nacionais.

O modelo é estimado por meio de uma abordagem bayesiana, que permite identificar automaticamente os pontos de mudança e estimar outros parâmetros.

Embora o modelo básico de decomposição aditiva pareça simples, os cálculos envolvidos são bastante complexos do ponto de vista matemático. Se não houver uma compreensão adequada de seu funcionamento, o modelo poderá produzir previsões incorretas.

O Prophet oferece duas abordagens de modelagem:

  1. Modelo de crescimento linear por partes (padrão)
  2. Modelo logístico

01. Modelo linear por partes

Essa é a abordagem utilizada por padrão no Prophet. Ela pressupõe que a tendência dos dados segue uma trajetória linear, mas pode mudar em determinados momentos, chamados pontos de mudança. É adequada para séries que apresentam crescimento ou queda relativamente constantes, mesmo quando ocorrem mudanças bruscas de tendência.

Essa abordagem é descrita pela fórmula apresentada na Figura 01.

02. Modelo logístico

Esse modelo é adequado para séries que apresentam crescimento com saturação: após uma fase inicial de crescimento acelerado, o ritmo diminui à medida que os valores se aproximam de um limite máximo. Esse comportamento é comum em sistemas reais sujeitos a restrições naturais ou empresariais, como o crescimento da base de usuários em um mercado próximo da saturação.

O modelo logístico inclui um parâmetro de capacidade que define o limite superior do crescimento.

Essa abordagem é descrita pela seguinte fórmula:

 

Imagem 02

em que:

representa a capacidade máxima, é a taxa de crescimento e é o parâmetro de deslocamento.


Implementação do modelo Prophet em Python

Utilizando dados do EURUSD no período gráfico de uma hora, vamos tentar identificar a tendência e a sazonalidade da série, além de prever valores futuros com o Prophet.

A primeira etapa é instalar todas as dependências listadas no arquivo requirements.txt, disponibilizado ao final do artigo.

pip install -r requirements.txt

Importação.

import pandas as pd
import numpy as np
import MetaTrader5 as mt5
import matplotlib.pyplot as plt
import seaborn as sns
from prophet import Prophet

plt.style.use('fivethirtyeight') 
sns.set_style("darkgrid")

Vamos obter os dados do MetaTrader 5.

if not mt5.initialize(r"c:\Program Files\MetaTrader 5 IC Markets (SC)\terminal64.exe"):
    print("Failed to initialize MetaTrader5. Error = ",mt5.last_error())
    mt5.shutdown()      

symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_H1

rates = mt5.copy_rates_from_pos(symbol, timeframe, 1, 10000)

if rates is None:
    print(f"Failed to copy rates for symbol={symbol}. MT5 Error = {mt5.last_error()}")

O Prophet exige uma coluna temporal com valores do tipo datetime (marcas de tempo), indispensável ao funcionamento do modelo.

Após obter os dados de cotação do MetaTrader 5, vamos convertê-los em um DataFrame do Pandas. Em seguida, converteremos a coluna de tempo, cujos valores estão expressos em segundos, para o formato datetime.

rates_df = pd.DataFrame(rates) # we convert rates object to a dataframe
rates_df["time"] = pd.to_datetime(rates_df["time"], unit="s") # we convert the time from seconds to datatime

rates_df

Resultados.

time open high low close tick_volume spread real_volume
0 2023-11-10 23:00:00 1.06849 1.06873 1.06826 1.06846 762 0 0
1 2023-11-13 00:00:00 1.06828 1.06853 1.06779 1.06841 1059 10 0
2 2023-11-13 01:00:00 1.06854 1.06907 1.06854 1.06906 571 0 0
3 2023-11-13 02:00:00 1.06904 1.06904 1.06822 1.06839 1053 0 0
4 2023-11-13 03:00:00 1.06840 1.06886 1.06811 1.06867 1204 0 0


O Prophet é um modelo univariado e exige apenas duas colunas no DataFrame: uma coluna do tipo datetime, denominada ds (date stamp), e a variável-alvo, denominada y.

Para começar, vamos criar um conjunto de dados simples a partir das informações obtidas do MetaTrader 5. Ele conterá duas colunas: time, com as marcas de tempo, e volatility, com os valores de volatilidade. Esses serão os dados utilizados nas próximas etapas da modelagem com o Prophet.

prophet_df = pd.DataFrame({
    "time": rates_df["time"],
    "volatility": rates_df["high"] - rates_df["low"]
}).set_index("time")

prophet_df

A volatilidade, calculada pela diferença entre o preço máximo e o mínimo, é utilizada como variável-alvo.

Diferentemente de outros modelos de previsão de séries temporais, como ARIMA e VAR (abordados anteriormente), que exigem que a variável-alvo seja estacionária, o Prophet não apresenta essa restrição. Ele também consegue trabalhar com dados não estacionários. No entanto, os modelos de machine learning geralmente apresentam melhores resultados com variáveis estacionárias, pois suas propriedades estatísticas permanecem constantes ao longo do tempo, incluindo a média, a variância e o desvio-padrão, o que facilita o treinamento.

Neste caso, optamos por uma variável-alvo estacionária para simplificar a tarefa.

Agora, vamos representar graficamente os dados do DataFrame e analisar seus atributos.

# Color pallete for plotting
color_pal = ["#F8766D", "#D39200", "#93AA00",
             "#00BA38", "#00C19F", "#00B9E3",
             "#619CFF", "#DB72FB"]

prophet_df.plot(figsize=(7,5), color=color_pal, title="Volatility (high-low) against time", ylabel="volatility", xlabel="time")

Resultados.

Figura 03

Também podemos organizar os dados em X, com os atributos temporais, e y, com a volatilidade, para avaliar a relação entre essas características e o comportamento do mercado.

def create_features(df, label=None):
    """
    Creates time series features from datetime index.
    """
    
    df = df.copy()
    df['date'] = df.index
    df['hour'] = df['date'].dt.hour
    df['dayofweek'] = df['date'].dt.dayofweek
    df['quarter'] = df['date'].dt.quarter
    df['month'] = df['date'].dt.month
    df['year'] = df['date'].dt.year
    df['dayofyear'] = df['date'].dt.dayofyear
    df['dayofmonth'] = df['date'].dt.day
    df['weekofyear'] = df['date'].dt.isocalendar().week
    
    X = df[['hour','dayofweek','quarter','month','year',
           'dayofyear','dayofmonth','weekofyear']]
    if label:
        y = df[label]
        return X, y
    return X

X, y = create_features(prophet_df, label='volatility')

features_and_target = pd.concat([X, y], axis=1)

Resultados.

hour dayofweek quarter month year dayofyear dayofmonth weekofyear volatility
time
2023-11-13 16:00:00 16 0 4 11 2023 317 13 46 0.00122
2023-11-13 17:00:00 17 0 4 11 2023 317 13 46 0.00179
2023-11-13 18:00:00 18 0 4 11 2023 317 13 46 0.00186
2023-11-13 19:00:00 19 0 4 11 2023 317 13 46 0.00125
2023-11-13 20:00:00 20 0 4 11 2023 317 13 46 0.00150


Podemos visualizar graficamente a relação entre esses atributos e a volatilidade para realizar uma análise manual.

sns.pairplot(features_and_target.dropna(),
             hue='hour',
             x_vars=['hour','dayofweek',
                     'year','weekofyear'],
             y_vars='volatility',
             height=5,
             plot_kws={'alpha':0.45, 'linewidth':0.5}
            )

plt.suptitle(f"{symbol} close prices by Hour, Day of Week, Year, and Week")
plt.show()

Resultados.


Figura 04

Observamos que a volatilidade das barras horárias apresenta variações associadas à hora do dia, ao dia da semana, ao ano e à semana do ano. Isso reforça nossa confiança de que os dados são adequados para utilização no modelo Prophet.

Treinamento do modelo Prophet

Vamos começar dividindo os dados em conjuntos de treinamento e teste.

split_date = '01-Jan-2025' # threshold date between training and testing samples, all values after this date are for testing 

prophet_df_train = prophet_df.loc[prophet_df.index <= split_date].copy().reset_index().rename(columns={"time": "ds", "volatility": "y"})
prophet_df_test = prophet_df.loc[prophet_df.index > split_date].copy().reset_index().rename(columns={"time": "ds", "volatility": "y"})

Em seguida, treinamos o modelo Prophet com os dados de treinamento.

model = Prophet()
model.fit(prophet_df_train)

Após o treinamento, normalmente é necessário avaliar o desempenho do modelo com dados fora da amostra de treinamento, ou seja, informações que ele ainda não utilizou. No entanto, o Prophet apresenta suas previsões em um formato diferente do utilizado por outros modelos.

test_fcst = model.predict(df=prophet_df_test)

Em vez de retornar apenas um vetor de valores previstos, o modelo retorna um DataFrame completo, com diversas colunas que contêm as previsões e informações sobre os componentes do modelo.

test_fcst.head()

Resultados.

	ds      trend   	yhat_lower      yhat_upper      trend_lower     trend_upper     additive_terms  additive_terms_lower    additive_terms_upper    daily   daily_lower     daily_upper     weekly  weekly_lower    weekly_upper    multiplicative_terms    multiplicative_terms_lower      multiplicative_terms_upper      yhat
0       2025-01-02 00:00:00     0.001674        0.000168        0.001993        0.001674        0.001674        -0.000571       -0.000571       -0.000571       -0.000510       -0.000510       -0.000510       -0.000061       -0.000061       -0.000061       0.0     0.0     0.0     0.001102
1       2025-01-02 01:00:00     0.001674        0.000161        0.001977        0.001674        0.001674        -0.000614       -0.000614       -0.000614       -0.000556       -0.000556       -0.000556       -0.000057       -0.000057       -0.000057       0.0     0.0     0.0     0.001060
2       2025-01-02 02:00:00     0.001674        0.000337        0.002123        0.001674        0.001674        -0.000483       -0.000483       -0.000483       -0.000430       -0.000430       -0.000430       -0.000054       -0.000054       -0.000054       0.0     0.0     0.0     0.001191

A tabela a seguir descreve o significado de algumas das colunas (atributos) retornadas pelo método predict.

Coluna Significado
ds Data e hora (marca de tempo) do ponto para o qual a previsão é gerada.
yhat Valor final previsto pelo modelo.
yhat_lower, yhat_upper Limites inferior e superior do intervalo de confiança (80% ou 95%) para yhat.
trend Valor do componente de tendência no instante ds (por exemplo, crescimento ou queda gradual).
trend_lower, trend_upper Limites do intervalo de confiança do componente de tendência.
additive_terms Soma de todos os componentes sazonais e dos efeitos dos feriados no instante ds (por exemplo, sazonalidade diária + semanal + efeitos dos feriados).
additive_terms_lower, additive_terms_upper Limites do intervalo de confiança dos componentes aditivos.
daily Componente de sazonalidade diária (por exemplo, padrões horários que se repetem ao longo do dia).
daily_lower, daily_upper Limites do intervalo de confiança do componente de sazonalidade diária.
weekly Componente de sazonalidade semanal (por exemplo, diferenças entre dias úteis e fins de semana).
weekly_lower, weekly_upper Limites do intervalo de confiança do componente de sazonalidade semanal.

As colunas mais importantes para nossa análise são yhat, yhat_lower, yhat_upper, trend, os componentes sazonais (daily, weekly, yearly), os efeitos dos feriados (quando presentes) e os limites inferior e superior dos intervalos de incerteza dos componentes (colunas com os sufixos _lower e _upper).

Vamos representar graficamente os valores reais e as previsões do conjunto de teste, incluindo também os valores reais do conjunto de treinamento.

f, ax = plt.subplots(figsize=(7,5))
ax.scatter(prophet_df_test["ds"], prophet_df_test['y'], color='r') # plot actual values from the testing sample in red

fig = model.plot(test_fcst, ax=ax) # plot the forecasts 

Resultado.

Figura 05

Os valores em preto correspondem ao conjunto de treinamento; em vermelho, estão os valores reais do conjunto de teste; e, em azul, as previsões do modelo para esse mesmo conjunto.

Esse gráfico não permite avaliar com clareza o desempenho do modelo. Por isso, vamos representar os valores reais e as previsões do conjunto de teste em gráficos separados.

Vamos avaliar o modelo com os dados de janeiro de 2025, o primeiro mês do conjunto de teste.

f, ax = plt.subplots(figsize=(7, 5))

ax.scatter(prophet_df_test["ds"], prophet_df_test['y'], color='r')
fig = model.plot(test_fcst, ax=ax)
ax.set_xbound(
            lower=pd.to_datetime("2025-01-01"), # starting data on the x axis
            upper=pd.to_datetime("2025-02-01")) # ending data on the x axis

ax.set_ylim(0, 0.005)
plot = plt.suptitle("January 2025, Actual vs Forecasts")

Resultados.


Figura 06

Pelo gráfico acima, observamos que o Prophet acerta algumas previsões, mas apresenta dificuldades para lidar com valores atípicos nos dados.

Podemos aprofundar a análise comparando os valores reais com as previsões do modelo durante a primeira semana de janeiro (de 1º a 8 de janeiro).

f, ax = plt.subplots(figsize=(9, 5))

ax.scatter(prophet_df_test["ds"], prophet_df_test['y'], color='r')
fig = model.plot(test_fcst, ax=ax)
ax.set_xbound(
            lower=pd.to_datetime("2025-01-01"),
            upper=pd.to_datetime("2025-01-08"))

ax.set_ylim(0, 0.005)
plot = plt.suptitle("January 01-08, 2025. Actual vs Forecasts")

Resultados.

Figura 07

O resultado é consideravelmente melhor. Ainda assim, embora o modelo consiga identificar determinados padrões, suas previsões não ficam tão próximas dos valores reais quanto gostaríamos. E essa proximidade é justamente o que buscamos em modelos de regressão.

Apesar disso, o modelo demonstra uma capacidade razoável de generalização.

Vamos avaliar seu desempenho utilizando métricas de qualidade.

import sklearn.metrics as metric

def forecast_accuracy(forecast, actual):
    # Convert to numpy arrays if they aren't already
    forecast = np.asarray(forecast)
    actual = np.asarray(actual)
    
    metrics = {
        'mape': metric.mean_absolute_percentage_error(actual, forecast),
        'me': np.mean(forecast - actual),  # Mean Error
        'mae': metric.mean_absolute_error(actual, forecast),
        'mpe': np.mean((forecast - actual) / actual),  # Mean Percentage Error
        'rmse': metric.root_mean_squared_error(actual, forecast),
        'minmax': 1 - np.mean(np.minimum(forecast, actual) / np.maximum(forecast, actual)),
        "r2_score": metric.r2_score(forecast, actual)
    }
    return metrics

results = forecast_accuracy(test_pred, prophet_df_test["y"])

for metric_name, value in results.items():
    print(f"{metric_name:<10}: {value:.6f}")

Resultados.

mape      : 0.603277
me        : 0.000130
mae       : 0.000829
mpe       : 0.430299
rmse      : 0.001221
minmax    : 0.339292
r2_score  : -4.547775

Neste caso, nosso principal interesse é a métrica MAPE (Mean Absolute Percentage Error, erro percentual absoluto médio). Um valor próximo de 0,6 indica que, em média, as previsões do modelo apresentam um desvio de 60% em relação aos valores reais. Em outras palavras, o modelo produz previsões muito imprecisas, com erros expressivos.


Inclusão de feriados no modelo Prophet

O Prophet foi desenvolvido desde o início para considerar eventos capazes de provocar alterações anômalas em séries temporais. No modelo, esses eventos são tratados como feriados.

Na prática, feriados e outros eventos especiais podem afetar os dados empresariais de maneira irregular. Alguns exemplos são:

  • Feriados nacionais (como Ano-Novo e Natal)
  • Eventos empresariais (como Black Friday e lançamentos de produtos)
  • Eventos financeiros (como comunicados de bancos centrais e encerramentos de trimestre)
  • Eventos locais (como eleições e fenômenos meteorológicos extremos)

Essas datas não seguem necessariamente um padrão sazonal regular, mas podem se repetir anualmente, trimestralmente, diariamente etc.

Em séries temporais financeiras utilizadas em trading, as notícias econômicas podem ser tratadas como feriados, pois provocam efeitos semelhantes. Isso pode ajudar o modelo a superar uma das limitações observadas até aqui: a dificuldade de levar em conta os valores extremos nas previsões.

Como mostra a fórmula do Prophet apresentada na Figura 01, considerar os efeitos dos feriados permite representar melhor o comportamento da série temporal, já que o componente de feriados é um dos elementos fundamentais do modelo.

Para isso, precisamos coletar as notícias econômicas. Vamos realizar essa etapa em MQL5.

Nome do arquivo: OHLC + News.mq5

input datetime start_date = D'01.01.2023';
input datetime end_date = D'24.6.2025';
input ENUM_TIMEFRAMES timeframe = PERIOD_H1;

MqlRates rates[];
struct news_data_struct
  {   
    datetime time[]; //News release time
    double open[]; //Candle opening price
    double high[]; //Candle high price
    double low[]; //Candle low price
    double close[]; //Candle close price
    string name[]; //Name of the news
    ENUM_CALENDAR_EVENT_SECTOR sector[]; //The sector a news is related to
    ENUM_CALENDAR_EVENT_IMPORTANCE importance[]; //Event importance
    double actual[]; //actual value
    double forecast[]; //forecast value
    double previous[]; //previous value
    
    void Resize(uint size)
      {
          ArrayResize(time, size);
          ArrayResize(open, size);
          ArrayResize(high, size);
          ArrayResize(low, size);
          ArrayResize(close, size);
          ArrayResize(name, size);
          ArrayResize(sector, size);
          ArrayResize(importance, size);
          ArrayResize(actual, size);
          ArrayResize(forecast, size);
          ArrayResize(previous, size);
      }
    
  } news_data;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---

   if (!ChartSetSymbolPeriod(0, Symbol(), timeframe))
      return;
   
   SaveNews(StringFormat("%s.%s.OHLC + News.csv",Symbol(),EnumToString(timeframe)));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//|   The function which collects news alongsided OHLC values and    |
//|   saves the data to a CSV file                                   |
//|                                                                  |
//+------------------------------------------------------------------+
void SaveNews(string csv_name)
 {
//--- get OHLC values first
   
   ResetLastError();
   if (CopyRates(Symbol(), timeframe, start_date, end_date, rates)<=0)
     {
       printf("%s failed to get price information from %s to %s. Error = %d",__FUNCTION__,string(start_date),string(end_date),GetLastError());
       return;
     }
      
   uint size = rates.Size();   
   news_data.Resize(size-1);

//---

   FileDelete(csv_name); //Delete an existing csv file of a given name
   int csv_handle = FileOpen(csv_name,FILE_WRITE|FILE_SHARE_WRITE|FILE_CSV|FILE_ANSI|FILE_COMMON,",",CP_UTF8); //csv handle
   
   if(csv_handle == INVALID_HANDLE)
     {
       printf("Invalid %s handle Error %d ",csv_name,GetLastError());
       return; //stop the process
     }
     
   FileSeek(csv_handle,0,SEEK_SET); //go to file begining
   FileWrite(csv_handle,"Time,Open,High,Low,Close,Name,Sector,Importance,Actual,Forecast,Previous"); //write csv header
   
   MqlCalendarValue values[]; //https://www.mql5.com/en/docs/constants/structures/mqlcalendar#mqlcalendarvalue
   for (uint i=0; i<size-1; i++)
      {
         news_data.time[i] = rates[i].time;
         news_data.open[i] = rates[i].open;
         news_data.high[i] = rates[i].high;
         news_data.low[i] = rates[i].low;
         news_data.close[i] = rates[i].close;
         
         int all_news = CalendarValueHistory(values, rates[i].time, rates[i+1].time, NULL, NULL); //we obtain all the news with their values https://www.mql5.com/en/docs/calendar/calendarvaluehistory
         
         for (int n=0; n<all_news; n++)
            {
              MqlCalendarEvent event;
              CalendarEventById(values[n].event_id, event); //Here among all the news we select one after the other by its id https://www.mql5.com/en/docs/calendar/calendareventbyid
                   
              MqlCalendarCountry country; //The couhtry where the currency pair originates
              CalendarCountryById(event.country_id, country); //https://www.mql5.com/en/docs/calendar/calendarcountrybyid
                 
              if (StringFind(Symbol(), country.currency)>-1) //We want to ensure that we filter news that has nothing to do with the base and the quote currency for the current symbol pair
                { 
                     news_data.name[i] = event.name;  
                     news_data.sector[i] = event.sector;
                     news_data.importance[i] = event.importance;
                       
                     news_data.actual[i] = !MathIsValidNumber(values[n].GetActualValue()) ? 0 : values[n].GetActualValue();
                     news_data.forecast[i] = !MathIsValidNumber(values[n].GetForecastValue()) ? 0 : values[n].GetForecastValue();
                     news_data.previous[i] = !MathIsValidNumber(values[n].GetPreviousValue()) ? 0 : values[n].GetPreviousValue();
                }
            }
          
          FileWrite(csv_handle,StringFormat("%s,%f,%f,%f,%f,%s,%s,%s,%f,%f,%f",
                                 (string)news_data.time[i],
                                 news_data.open[i],
                                 news_data.high[i],
                                 news_data.low[i],
                                 news_data.close[i],
                                 news_data.name[i],
                                 EnumToString(news_data.sector[i]),
                                 EnumToString(news_data.importance[i]),
                                 news_data.actual[i],
                                 news_data.forecast[i],
                                 news_data.previous[i]
                               ));
       }  
//---

   FileClose(csv_handle);
 }

Após a coleta das notícias, a função SaveNews salva os dados em um arquivo CSV na pasta compartilhada.

No script Python, esses dados são carregados a partir do mesmo diretório.

from Trade.TerminalInfo import CTerminalInfo
import os

terminal = CTerminalInfo()
data_path = os.path.join(terminal.common_data_path(), "Files")

timeframe = "PERIOD_H1"
df = pd.read_csv(os.path.join(data_path, f"{symbol}.{timeframe}.OHLC + News.csv"))

df

Resultados.

Time Open High Low Close Name Sector Importance Actual Forecast Previous
0 2023.01.02 01:00:00 1.06967 1.06983 1.06927 1.06983 New Year's Day CALENDAR_SECTOR_HOLIDAYS CALENDAR_IMPORTANCE_NONE 0.0 0.0 0.0
1 2023.01.02 02:00:00 1.06984 1.07059 1.06914 1.07041 New Year's Day CALENDAR_SECTOR_HOLIDAYS CALENDAR_IMPORTANCE_NONE 0.0 0.0 0.0
2 2023.01.02 03:00:00 1.07059 1.07069 1.06858 1.06910 New Year's Day CALENDAR_SECTOR_HOLIDAYS CALENDAR_IMPORTANCE_NONE 0.0 0.0 0.0
3 2023.01.02 04:00:00 1.06909 1.06909 1.06828 1.06880 New Year's Day CALENDAR_SECTOR_HOLIDAYS CALENDAR_IMPORTANCE_NONE 0.0 0.0 0.0
4 2023.01.02 05:00:00 1.06881 1.07029 1.06880 1.06897 New Year's Day CALENDAR_SECTOR_HOLIDAYS CALENDAR_IMPORTANCE_NONE 0.0 0.0 0.0


Como o script MQL5 coleta notícias para cada linha dos dados, a coluna correspondente também apresenta valores null nos períodos em que não há notícias. Essas linhas precisam ser filtradas.

news_df = df[df['Name'] != "(null)"].copy()

Assim como estruturamos o conjunto de dados principal em duas colunas (ds e y), o conjunto de dados dos feriados também deve conter duas colunas: ds e holiday. A coluna holiday armazena os nomes das notícias.

holidays = news_df[['Time', 'Name']].rename(columns={
    'Time': 'ds',
    'Name': 'holiday'
})

holidays['ds'] = pd.to_datetime(holidays['ds'])  # Ensure datetime format
holidays

Resultados.

ds holiday
0 2023-01-02 01:00:00 New Year's Day
1 2023-01-02 02:00:00 New Year's Day
2 2023-01-02 03:00:00 New Year's Day
3 2023-01-02 04:00:00 New Year's Day
4 2023-01-02 05:00:00 New Year's Day


Além disso, o DataFrame de feriados pode incluir duas colunas opcionais: lower_window e upper_window. Essas colunas indicam ao modelo como um evento influencia os dados antes e depois de sua ocorrência.

Na prática, a influência de todos os eventos raramente se limita ao momento de sua divulgação — muitas vezes se estende aos períodos anteriores e posteriores.

holidays['lower_window'] = 0
holidays['upper_window'] = 1  # Extend effect to 1 hour after

A coluna lower_window define a influência do evento antes de sua ocorrência, enquanto a coluna upper_window define sua influência sobre a série temporal após o evento.

  • Na coluna lower_window, os valores podem ser iguais ou inferiores a zero (≤ 0), sendo 0 o valor padrão, o que significa que o feriado não afeta os valores anteriores da série temporal. O valor -1 indica que o evento influenciou uma barra anterior, e assim por diante.
  • Na coluna upper_window, os valores podem ser iguais ou superiores a zero (≥ 0), e o valor padrão também é 0, indicando que o evento não afeta os valores futuros da série temporal. O valor 1 indica uma influência sobre a barra seguinte, e assim por diante.

Agora, vamos adicionar esses atributos ao modelo.

holidays['lower_window'] = -1 # The anticipation of the news affect the volatility 1 bar before it's release
holidays['upper_window'] = 1  # The news affects the volatility 1 bar after its release

holidays

Agora, nosso DataFrame com os dados dos feriados apresenta a seguinte estrutura:

ds holiday lower_window upper_window
0 2023-01-02 01:00:00 New Year's Day -1 1
1 2023-01-02 02:00:00 New Year's Day -1 1
2 2023-01-02 03:00:00 New Year's Day -1 1
3 2023-01-02 04:00:00 New Year's Day -1 1
4 2023-01-02 05:00:00 New Year's Day -1 1
... ... ... ... ...
15369 2025-06-20 18:00:00 Eurogroup Meeting -1 1
15370 2025-06-20 19:00:00 Eurogroup Meeting -1 1
15371 2025-06-20 20:00:00 Eurogroup Meeting -1 1
15372 2025-06-20 21:00:00 Eurogroup Meeting -1 1
15373 2025-06-20 22:00:00 Eurogroup Meeting -1 1


Vamos configurar o Prophet com o DataFrame de feriados e treiná-lo com os dados preparados anteriormente.

model_w_holidays = Prophet(holidays=holidays)
model_w_holidays.fit(prophet_df_train)

Agora, vamos avaliar as previsões do modelo considerando as notícias, representando graficamente os valores previstos e reais, como fizemos antes.

# Predict on training set with model
test_fcst = model_w_holidays.predict(df=prophet_df_test)

test_pred = test_fcst.yhat # We get the predictions

# Plot the forecast with the actuals

f, ax = plt.subplots(figsize=(10,5))
ax.scatter(prophet_df_test["ds"], prophet_df_test['y'], color='r')

fig = model_w_holidays.plot(test_fcst, ax=ax)

Resultados.

Figura 08

Em comparação com o modelo sem notícias (Figura 05), que produzia previsões com pouca variação, o novo modelo consegue captar melhor as oscilações que antes não eram consideradas.

Vamos avaliar novamente seu desempenho utilizando as mesmas métricas.

results = forecast_accuracy(test_pred, prophet_df_test["y"])

for metric_name, value in results.items():
    print(f"{metric_name:<10}: {value:.6f}")

Resultados.

mape      : 0.549152
me        : -0.000633
mae       : 0.000970
mpe       : -0.175082
rmse      : 0.001487
minmax    : 0.461444
r2_score  : -2.793478

A métrica MAPE indica uma redução relativa de aproximadamente 8% no erro das previsões. O valor passou de cerca de 60% para 55%, uma queda de cinco pontos percentuais. Essa melhoria também é confirmada pela métrica r2_score.

Ainda assim, um modelo com erro de 55% não pode ser considerado satisfatório. Um bom modelo deve apresentar um erro inferior a 50% (< 50%). Portanto, ainda podemos melhorar seu desempenho por meio de um tratamento mais adequado dos feriados e das notícias econômicas.

Neste exemplo, definimos lower_window como -1 e upper_window como 1, indicando que o modelo considera os efeitos da notícia sobre a volatilidade em uma janela que se estende de uma barra antes até uma barra depois de sua divulgação. Embora essa configuração tenha melhorado os resultados, não podemos afirmar que seja a mais adequada.

Sabemos que as notícias variam tanto na intensidade quanto na duração de seus efeitos. Por isso, utilizar os mesmos valores para todos os eventos não é uma abordagem conceitualmente correta. Além disso, o modelo considerou todas as notícias, inclusive as de baixa importância, que geralmente são ignoradas pelos traders por ocorrerem com muita frequência e terem efeitos difíceis de identificar no gráfico.

Para resolver esses dois problemas, precisamos definir os valores de lower_window e upper_window dinamicamente, de acordo com o tipo de notícia e seu impacto observado historicamente.

Exemplo de pseudocódigo.

def get_windows(name):
    if "CPI" in name:
        return (-1, 4) # CPI news affects one previous bar volatility, and it affects the volatility of four bars ahead (4 hours impact forward)
    elif "NFP" in name:
        return (-1, 2) # NFP news affects one previous bar volatility, and it affects the volatility of two bars ahead (2 hours impact afterward)
    elif "FOMC" in name or "Rate" in name:
        return (-2, 6) # NFP news affects two previous bar volatility, and it affects the volatility of six bars ahead (6 hours impact afterward)
    else:
        return (0, 1)  # Default


holidays[['lower_window', 'upper_window']] = holidays['holiday'].apply(
    lambda name: pd.Series(get_windows(name))
)

Como existem dezenas de milhares de tipos diferentes de notícias e precisamos determinar com precisão os parâmetros que representam seus efeitos, essa abordagem é extremamente complexa de implementar. No entanto, é a solução mais adequada do ponto de vista conceitual. Por isso, essa etapa ficará a cargo do leitor.

Por enquanto, uma solução razoável é filtrar as notícias e manter apenas os eventos de importância alta e moderada.

news_df = df[
    (df['Name'] != "(null)") & # Filter rows without news at all
    ((df['Importance'] == "CALENDAR_IMPORTANCE_HIGH") | (df['Importance'] == "CALENDAR_IMPORTANCE_MODERATE")) # Filter other news except high importance news
].copy()

news_df

Resultados.

Time Open High Low Close Name Sector Importance Actual Forecast Previous
7 2023.01.02 08:00:00 1.06921 1.06973 1.06724 1.06858 S&P Global Manufacturing PMI CALENDAR_SECTOR_BUSINESS CALENDAR_IMPORTANCE_MODERATE 47.10 47.400 47.400
8 2023.01.02 09:00:00 1.06878 1.06909 1.06627 1.06784 S&P Global Manufacturing PMI CALENDAR_SECTOR_BUSINESS CALENDAR_IMPORTANCE_MODERATE 47.80 47.800 47.800
31 2023.01.03 08:00:00 1.06636 1.06677 1.06514 1.06524 Unemployment CALENDAR_SECTOR_JOBS CALENDAR_IMPORTANCE_MODERATE 2.52 2.522 2.538
37 2023.01.03 14:00:00 1.05283 1.05490 1.05241 1.05355 S&P Global Manufacturing PMI CALENDAR_SECTOR_BUSINESS CALENDAR_IMPORTANCE_HIGH 46.20 46.200 46.200
38 2023.01.03 15:00:00 1.05353 1.05698 1.05304 1.05602 Construction Spending m/m CALENDAR_SECTOR_HOUSING CALENDAR_IMPORTANCE_MODERATE 0.20 0.200 -0.300


Em seguida, selecionamos as colunas com a data e a hora e com o nome do evento, montamos o DataFrame holidays e acrescentamos os valores de lower_window e upper_window.

holidays = news_df[['Time', 'Name']].rename(columns={
    'Time': 'ds',
    'Name': 'holiday'
})

holidays['ds'] = pd.to_datetime(holidays['ds'])  # Ensure datetime format

holidays['lower_window'] = 0
holidays['upper_window'] = 1 

holidays

Após treinar novamente o modelo, o gráfico abaixo apresenta os valores do conjunto de treinamento em preto, os valores reais do conjunto de teste em vermelho e as previsões do modelo para o conjunto de teste em azul.

Figura 09

O desempenho do modelo melhorou mais uma vez. Segundo a métrica MAPE, o erro caiu para aproximadamente 50%. Com esse resultado, o modelo de regressão já pode ser utilizado para gerar previsões.

mape      : 0.506827
me        : -0.000053
mae       : 0.000783
mpe       : 0.271597
rmse      : 0.001234
minmax    : 0.320422
r2_score  : -3.318859

Vale observar que as notícias foram importadas separadamente de um arquivo CSV, enquanto os dados de treinamento foram obtidos diretamente do MetaTrader 5.

O Prophet alinha (sincroniza) automaticamente as datas do DataFrame holidays com as do conjunto de treinamento principal, desde que as marcas de tempo de holidays estejam dentro do intervalo de treinamento ou de previsão.

Apesar dessa sincronização automática, é importante garantir que ambos os conjuntos de dados tenham a mesma data inicial para obter o melhor desempenho possível.

Neste caso, foi necessário voltar à etapa de obtenção dos dados de preços do MetaTrader 5, no arquivo main.ipynb, e ajustar as datas inicial e final para que coincidissem com as utilizadas no script OHLC + News.mq5.

# set time zone to UTC
timezone = pytz.timezone("Etc/UTC")

# create 'datetime' objects in UTC-time to avoid the implementation of a local time zone offset
utc_from = datetime(2023, 1, 1, tzinfo=timezone)
utc_to = datetime(2025, 6, 24, hour = 0, tzinfo=timezone)

rates = mt5.copy_rates_range(symbol, timeframe, utc_from, utc_to)


Criação de um EA para MetaTrader 5 baseado no modelo Prophet

Para criar um EA baseado no Prophet, precisamos gerar previsões em tempo real para a variável-alvo, que, neste caso, é a volatilidade.

Isso exige a implementação de um pipeline capaz de obter dados de mercado atualizados para os símbolos de interesse, além das notícias mais recentes. No script de treinamento main.ipynb, os dados foram extraídos do MetaTrader 5 por meio do pacote MetaTrader 5-Python. No entanto, como esse pacote não permite obter notícias, precisamos recorrer ao MQL5.

A proposta é estabelecer uma troca de dados entre o script Python e o EA desenvolvido em MQL5.

  • O EA Data for Prophet.mq5, executado em um gráfico do MetaTrader 5, salva periodicamente os dados de notícias e os valores OHLC em um arquivo CSV na pasta compartilhada.
  • Em seguida, o script Python Prophet-trading-bot.py lê esse arquivo e treina periodicamente o modelo Prophet.
  • Após o treinamento, o modelo gera previsões que são utilizadas para tomar decisões de negociação no próprio script Python.

Arquivo: Data for Prophet.mq5

input uint collect_news_interval_seconds = 60;
input uint training_bars = 1000;

input ENUM_TIMEFRAMES timeframe = PERIOD_H1;
//... other lines of code
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {  
//--- create timer

   EventSetTimer(collect_news_interval_seconds);
   
   if (!ChartSetSymbolPeriod(0, Symbol(), timeframe))
      return INIT_FAILED;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---   
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   MqlDateTime time_struct;
   TimeToStruct(TimeGMT(), time_struct);
   
   SaveNews(StringFormat("%s.%s.OHLC.date=%s.hour=%d + News.csv",Symbol(),EnumToString(timeframe), TimeToString(TimeGMT(), TIME_DATE), time_struct.hour));
  }

Para garantir que o arquivo correto seja utilizado, seu nome inclui a data e a hora atuais em UTC.

Por padrão, esse EA coleta notícias e outros dados e os salva em um arquivo CSV a cada minuto, conforme definido na função OnTimer.

No script Python, carregamos os dados do arquivo CSV salvo na pasta compartilhada.

Arquivo: Prophet-trading-bot.py

def prophet_vol_predict() -> float:

    # Getting the data with news
    
    now_utc = datetime.utcnow()
    current_date = now_utc.strftime("%Y.%m.%d")
    current_hour = now_utc.hour

    filename = f"{symbol}.{timeframe}.OHLC.date={current_date}.hour={current_hour} + News.csv" # the same file naming as in MQL5 script
    common_path = os.path.join(terminal.common_data_path(), "Files")
    csv_path = os.path.join(common_path, filename)
    
    # Keep trying to read a CSV file until it is found, as there could be a temporary difference in values for the file due to the change in time
    while True:
        if os.path.exists(csv_path):
            try:
                rates_df = pd.read_csv(csv_path)
                rates_df["Time"] = pd.to_datetime(rates_df["Time"], unit="s", errors="ignore")  # Convert time from seconds to datetime
                
                print("File loaded successfully.")
                break  # Exit the loop once file is read
            except Exception as e:
                print(f"Error reading the file: {e}")
                time.sleep(30)
        else:
            print("File not found. Retrying in 30 seconds...")
            time.sleep(30)
 

Em seguida, preparamos a coluna de volatilidade e extraímos os nomes das notícias para montar, respectivamente, o conjunto de dados de treinamento e o DataFrame de feriados.

# Getting continous variables for the prophet model
    
prophet_df = pd.DataFrame({
    "time": rates_df["Time"],
    "volatility": rates_df["High"] - rates_df["Low"]
}).set_index("time")

prophet_df = prophet_df.reset_index().rename(columns={"time": "ds", "volatility": "y"}).copy()
    
print("Prophet df\n",prophet_df.head())
    
# Getting the news data for the model as well
    
news_df = rates_df[
    (rates_df['Name'] != "(null)") & # Filter rows without news at all
    ((rates_df['Importance'] == "CALENDAR_IMPORTANCE_HIGH") | (rates_df['Importance'] == "CALENDAR_IMPORTANCE_MODERATE")) # Filter other news except high importance news
].copy()

holidays = news_df[['Time', 'Name']].rename(columns={
    'Time': 'ds',
    'Name': 'holiday'
})

holidays['ds'] = pd.to_datetime(holidays['ds'])  # Ensure datetime format

holidays['lower_window'] = 0
holidays['upper_window'] = 1 

print("Holidays df\n", holidays)

Na função prophet_vol_pred, o modelo é treinado com os dados obtidos. Em seguida, a função retorna um único valor previsto: a volatilidade esperada para a próxima barra.

    # re-training the prophet model
    
    prophet_model = Prophet(holidays=holidays)
    prophet_model.fit(prophet_df)
    
    # Making future predictions
    
    future = prophet_model.make_future_dataframe(periods=1) # prepare the dataframe for a single value prediction
    forecast = prophet_model.predict(future) # Predict the next one value
    
    return forecast.yhat[0] # return a single predicted value

Assim como ocorre com outros modelos de machine learning utilizados na previsão de séries temporais, o Prophet precisa ser atualizado regularmente para incorporar informações recentes que possam influenciar as previsões futuras. Por isso, o modelo é retreinado antes de cada nova previsão.

Vamos executar a função e analisar o resultado.

print("predicted volatility: ",prophet_vol_predict())

Resultados.

File loaded successfully.
Prophet df
                     ds        y
0  2025.04.29 01:00:00  0.00100 
1  2025.04.29 02:00:00  0.00210 
2  2025.04.29 03:00:00  0.00170 
3  2025.04.29 04:00:00  0.00215 
4  2025.04.29 05:00:00  0.00278 
Holidays df
                      ds                                     holiday  lower_window  upper_window
8   2025-04-29 09:00:00                        GfK Consumer Climate             0             1 
14  2025-04-29 15:00:00          Retail Inventories excl. Autos m/m             0             1 
31  2025-04-30 08:00:00                       Consumer Spending m/m             0             1 
33  2025-04-30 10:00:00                                Unemployment             0             1 
35  2025-04-30 12:00:00                                     GDP y/y             0             1 
..                  ...                                         ...           ...           ... 
978 2025-06-24 19:00:00                 FOMC Member Williams Speech             0             1
979 2025-06-24 20:00:00                         2-Year Note Auction             0             1
982 2025-06-24 23:00:00  Fed Vice Chair for Supervision Barr Speech             0             1
984 2025-06-25 01:00:00                            Jobseekers Total             0             1
994 2025-06-25 11:00:00  Bbk Executive Board Member Mauderer Speech             0             1

[186 rows x 4 columns]
16:01:50 - cmdstanpy - INFO - Chain [1] start processing
16:01:50 - cmdstanpy - INFO - Chain [1] done processing
predicted volatility:  0.0013592111956094713

Agora que conseguimos obter a volatilidade prevista, podemos utilizá-la em uma estratégia de negociação.

symbol = "EURUSD"
timeframe = "PERIOD_H1"
terminal = CTerminalInfo()
m_position = CPositionInfo()

def main():
    
    m_symbol = CSymbolInfo(symbol=symbol)
    
    magic_number = 25062025
    slippage = 100
    
    m_trade = CTrade(magic_number=magic_number,
                     filling_type_symbol=symbol,
                     deviation_points=slippage)
    
    m_symbol.refresh_rates() # Get recent information from the market
    
    # we want to open random buy and sell trades if they don't exist and use the predicted volatility to set our stoploss and takeprofit targets
    
    predicted_volatility = prophet_vol_predict()
    print("predicted volatility: ",prophet_vol_predict())
    
    if pos_exists(mt5.POSITION_TYPE_BUY, magic_number, symbol) is False:
        m_trade.buy(volume=m_symbol.lots_min(), 
                    symbol=symbol,
                    price=m_symbol.ask(),
                    sl=m_symbol.ask()-predicted_volatility,
                    tp=m_symbol.ask()+predicted_volatility)
        
    if pos_exists(mt5.POSITION_TYPE_SELL, magic_number, symbol) is False:
        m_trade.sell(volume=m_symbol.lots_min(), 
                     symbol=symbol,
                     price=m_symbol.bid(),
                     sl=m_symbol.bid()+predicted_volatility,
                     tp=m_symbol.bid()-predicted_volatility)

Essa função recebe a volatilidade prevista pelo modelo Prophet e a utiliza para definir os níveis de stop loss e take profit das operações. Antes de abrir uma nova operação, verificamos se não há outra posição aberta do mesmo tipo.

Chamada da função.

main()

Resultado.

 

Figura 10

No MetaTrader 5, foram abertas duas operações em direções opostas, com níveis de stop loss e take profit calculados com base na volatilidade prevista.

Podemos automatizar o treinamento e monitorar regularmente as operações e os sinais de negociação.

schedule.every(1).minute.do(main) # train and run trading operations after every one minute

while True:
    
    schedule.run_pending()
    time.sleep(1)



Conclusão

Embora muitos artigos, publicações e tutoriais afirmem que o Prophet é uma boa opção para a previsão de séries temporais, nos testes realizados ao longo desta série ele apresentou um dos piores desempenhos entre os modelos avaliados.

O Prophet pode ser útil em tarefas de previsão relativamente simples, como estimar a demanda de uma empresa em função das condições climáticas, dos feriados ou de fatores sazonais. No entanto, os mercados financeiros são muito mais complexos. Como mostram os gráficos das Figuras 05 a 09, que comparam os valores reais e previstos no conjunto de teste, o modelo Prophet não consegue prever com precisão a maior parte dos valores.

É provável que existam maneiras de melhorar seu desempenho, mas, por enquanto, recomendo utilizá-lo apenas em tarefas relativamente simples.

Principais limitações do modelo:

  • Estrutura simples, que não permite representar interações complexas entre os dados.
  • Baixo desempenho na previsão de volatilidade: como vimos, o modelo apresenta dificuldades ao trabalhar com dados do mercado Forex.
  • Ausência de modelagem multivariada: na configuração apresentada, o modelo utiliza apenas duas colunas, uma para o tempo e outra para a variável-alvo.
  • Ausência de mecanismos integrados de validação cruzada e ajuste de hiperparâmetros: os ajustes relacionados à tendência, à sazonalidade e aos pontos de mudança precisam ser realizados manualmente.

Boa sorte a todos!


Fontes e referências


Tabela de arquivos anexos

Nome do arquivo Descrição e finalidade
Python code\main.ipynb Notebook Jupyter para análise de dados e estudo do modelo Prophet.
Python code\Prophet-trading-bot.py Robô de negociação desenvolvido em Python para MetaTrader 5.
Python code\requirementx.txt Arquivo de texto com as dependências Python e suas respectivas versões.
Python code\error_description.py Contém as descrições de todos os códigos de erro gerados pelo MetaTrader 5.
Python code\Trade\* Contém classes de negociação (CTrade, CPositionInfo etc.) para Python, semelhantes às disponíveis no MQL5.
Experts\Data for Prophet.mq5 EA que coleta periodicamente os dados necessários para o treinamento do modelo Prophet e os salva em um arquivo CSV.
Scripts\OHLC + News.mq5 Script que coleta e salva em um arquivo CSV os dados necessários para o treinamento do modelo Prophet.

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

Arquivos anexados |
Attachments.zip (803.62 KB)
Ciência de Dados e ML (Parte 42): Previsão de Séries Temporais no Mercado Forex com ARIMA em Python Ciência de Dados e ML (Parte 42): Previsão de Séries Temporais no Mercado Forex com ARIMA em Python
ARIMA, sigla para Modelo Autorregressivo Integrado de Médias Móveis, é um poderoso modelo estatístico tradicional de previsão de séries temporais. Com a capacidade de identificar picos e flutuações nos dados de uma série temporal, esse modelo pode prever com precisão os próximos valores. Neste artigo, vamos entender o que é o ARIMA, como funciona, como utilizá-lo para prever os próximos preços do mercado com alta precisão e muito mais.
Desenvolvimento de ferramentas para análise de Price Action (Parte 29): EA "Boom and Crash Interceptor" Desenvolvimento de ferramentas para análise de Price Action (Parte 29): EA "Boom and Crash Interceptor"
Descubra como o EA Boom & Crash Interceptor transforma seus gráficos em um sistema proativo de alertas, capaz de identificar movimentos explosivos dos preços por meio da análise de velocidade, da detecção de picos de volatilidade, da confirmação de tendência e de filtros de zonas de pivô. Com setas verdes para "Boom" e vermelhas para "Crash", o EA facilita a tomada de decisões ao filtrar o ruído do mercado e ajudar a aproveitar melhor os movimentos bruscos dos preços. Vamos entender como ele funciona e por que essa ferramenta pode se tornar um diferencial importante nas suas operações.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
A arte de trabalhar com logs (Parte 9): aplicando o padrão Builder e definindo configurações padrão A arte de trabalhar com logs (Parte 9): aplicando o padrão Builder e definindo configurações padrão
Este artigo mostra como simplificar radicalmente o uso da biblioteca Logify por meio do padrão "Builder" e de configurações padrão automáticas. São abordados a estrutura de classes Builder especializadas, o uso dessas classes com recursos de preenchimento automático inteligente (autocompletar) e as formas de garantir que o registro de logs funcione mesmo sem configuração manual. Além disso, o artigo apresenta as adaptações necessárias para a build 5100 do MetaTrader 5.