Русский
preview
Indicador de sazonalidade por horas, dias da semana e meses

Indicador de sazonalidade por horas, dias da semana e meses

MetaTrader 5Sistemas de negociação |
40 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Os preços tocam uma melodia que se repete em determinados dias do mês, dias da semana ou até mesmo horas do dia. Esses ritmos repetitivos, ou padrões sazonais, podem se tornar para o trader uma pista de quando o mercado tende a subir e quando tende a cair. A sazonalidade nos mercados financeiros não é apenas um fenômeno curioso, mas uma ferramenta que ajuda a encontrar momentos previsíveis no caos dos preços. Por exemplo, você já percebeu que alguns pares de moedas frequentemente sobem às segundas-feiras ou caem no final do mês? Isso é sazonalidade, e estudá-la pode dar aos traders uma vantagem.

Neste guia criaremos um indicador de sazonalidade na linguagem MQL5 para a plataforma MetaTrader 5. Nosso indicador analisará dados históricos de preços para identificar a rentabilidade média dos dias do mês (de 1 a 31), dos dias da semana (de segunda-feira a domingo) ou das horas do dia (de 0 a 23). Os resultados serão exibidos na forma de um histograma em uma janela separada do gráfico, com uma linha de previsão conectando os valores de sazonalidade e pontos destacando os valores esperados nas próximas barras. Além disso, o indicador exibirá estatísticas textuais com previsões, melhores e piores períodos. Vamos destrinchar o processo de desenvolvimento passo a passo, explicando cada parte do código para que você não apenas possa usar o indicador, mas também adaptá-lo às suas próprias ideias. Será uma jornada pelo mundo da programação e da análise financeira, onde o código se torna uma ponte entre dados e decisões de trading.


Para que estudar a sazonalidade

A sazonalidade é como um pulso oculto do mercado. Ela surge devido a diversos fatores, comportamento dos traders, eventos econômicos e até psicologia humana. Por exemplo, no final do mês grandes fundos podem fechar posições, provocando movimentos previsíveis de preços. Ou em determinadas horas do dia a atividade do mercado aumenta devido à abertura das sessões de negociação em Londres ou Nova York. Nosso indicador ajudará a identificar esses padrões, transformando dados caóticos em gráficos e números compreensíveis. Isso não é magia, mas ciência de dados trabalhando a favor do trader.

O indicador que criaremos será universal. Ele permitirá escolher o tipo de sazonalidade, configurar o número de barras analisadas, exibir a rentabilidade em porcentagens ou valores absolutos, além de ativar ou desativar a previsão no gráfico. A flexibilidade é a chave para que o indicador se torne uma ferramenta útil para diferentes mercados e timeframes, seja um gráfico diário de EUR/USD ou um gráfico horário do ouro.


Definimos a estrutura do indicador

A criação do indicador começa com o entendimento de como ele será exibido e como funcionará. Queremos que ele seja mostrado em uma janela separada abaixo do gráfico de preços, para não sobrecarregar o espaço principal. Para a visualização utilizamos três elementos: um histograma que mostrará a rentabilidade média para cada período, uma linha de previsão que conectará os valores de sazonalidade e os dados previstos, e pontos de previsão que destacam os valores esperados nas próximas duas barras. Cada elemento estará associado a um buffer de dados, um array que armazena valores para renderização. Também precisaremos de variáveis globais para armazenar os dados de sazonalidade e os rótulos dos períodos, além de parâmetros de entrada para que o usuário possa configurar o indicador.

//+------------------------------------------------------------------+
//|                                         SeasonalityIndicator.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

//--- plot Seasonality
#property indicator_label1  "Seasonality"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- plot Forecast Line
#property indicator_label2  "Forecast"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_DASH
#property indicator_width2  2

//--- plot Forecast Points
#property indicator_label3  "Forecast Points"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrOrange
#property indicator_width3  3

Neste código definimos as informações principais do indicador: direitos autorais, o link para o site da MetaQuotes e a versão. A diretiva #property indicator_separate_window indica que o indicador será exibido em uma janela separada. Definimos três buffers e três gráficos. O histograma (DRAW_HISTOGRAM) é colorido em azul claro; a linha de previsão (DRAW_LINE), em vermelho pontilhado; e os pontos de previsão (DRAW_ARROW), em laranja com largura de três pixels. Essas configurações definem o estilo visual que tornará o indicador claro e visualmente atraente.


Configuramos os parâmetros de entrada

Para que o indicador seja flexível, adicionamos parâmetros de entrada que o usuário pode alterar na janela de configurações do MetaTrader 5. O parâmetro para escolher o tipo de sazonalidade permite analisar dias do mês, dias da semana ou horas do dia. A quantidade de barras para análise determina quão profundamente examinaremos o histórico. Existe a possibilidade de exibir a rentabilidade em porcentagens ou valores absolutos, além de mostrar apenas valores positivos de sazonalidade. O parâmetro para ativar a previsão no gráfico e configurar as cores da linha e dos pontos de previsão torna o indicador ainda mais configurável.

//--- Входные параметры
enum ENUM_SEASONALITY_TYPE
{
   SEASONALITY_DAYS_OF_MONTH = 0,  // Дни месяца (1-31)
   SEASONALITY_DAYS_OF_WEEK = 1,   // Дни недели (Пн-Вс)
   SEASONALITY_HOURS = 2           // Часы (0-23)
};

input ENUM_SEASONALITY_TYPE SeasonalityType = SEASONALITY_DAYS_OF_MONTH; // Тип сезонности
input int BarsToAnalyze = 1000;                                          // Количество баров для анализа
input bool ShowPercentage = true;                                        // Показывать в процентах
input bool ShowPositiveOnly = false;                                     // Показывать только положительные значения
input bool ShowForecastOnChart = true;                                   // Показывать прогноз на графике
input color ForecastColor = clrRed;                                      // Цвет линии прогноза
input color ForecastPointsColor = clrOrange;                             // Цвет точек прогноза

A enumeração ENUM_SEASONALITY_TYPE define três opções de sazonalidade. Por padrão é selecionada a análise por dias do mês. O parâmetro BarsToAnalyze está definido como 1000 barras, o que corresponde aproximadamente a quatro anos de dados em um gráfico diário. O parâmetro ShowPercentage está ativado por padrão para que a rentabilidade seja exibida em porcentagens, pois isso é mais intuitivo. Os parâmetros ShowPositiveOnly e ShowForecastOnChart oferecem flexibilidade adicional, enquanto as cores podem ser alteradas pela interface do MetaTrader.


Definimos os buffers e as variáveis

Para armazenar os dados o indicador utiliza três buffers: um para o histograma, outro para a linha de previsão e o terceiro para os pontos de previsão. Também são necessárias variáveis globais: um array para armazenar a rentabilidade média por período, um array para os rótulos dos períodos (por exemplo, "Seg" ou "1") e uma variável que define a quantidade de períodos, que depende do tipo de sazonalidade (31 para dias do mês, 7 para dias da semana, 24 para horas).

//--- Буферы индикатора
double SeasonalityBuffer[];
double ForecastLineBuffer[];
double ForecastPointsBuffer[];

//--- Глобальные переменные
double seasonality_data[];
string period_labels[];
int periods_count;

Os buffers conterão os dados para renderização, enquanto as variáveis seasonality_data e period_labels ajudarão a armazenar e exibir os resultados da análise. A variável periods_count define quantos períodos serão analisados e é configurada de acordo com o tipo de sazonalidade.


Inicialização do indicador

A função OnInit é executada quando o indicador é iniciado e é responsável pela configuração inicial. Ela conecta os buffers aos gráficos, define o estilo das setas para os pontos de previsão e aplica as cores definidas nos parâmetros do usuário. Dependendo do tipo de sazonalidade são criados os rótulos dos períodos, e o array para armazenamento dos dados de sazonalidade é inicializado com zeros. A precisão da exibição é ajustada conforme a escolha entre porcentagens ou valores absolutos.

int OnInit()
{
   SetIndexBuffer(0, SeasonalityBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, ForecastLineBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, ForecastPointsBuffer, INDICATOR_DATA);
   
   PlotIndexSetInteger(2, PLOT_ARROW, 159);
   
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, ForecastColor);
   PlotIndexSetInteger(2, PLOT_LINE_COLOR, ForecastPointsColor);
   
   switch(SeasonalityType)
   {
      case SEASONALITY_DAYS_OF_MONTH:
         periods_count = 31;
         ArrayResize(period_labels, periods_count);
         for(int i = 0; i < periods_count; i++)
            period_labels[i] = IntegerToString(i + 1);
         IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по дням месяца");
         break;
         
      case SEASONALITY_DAYS_OF_WEEK:
         periods_count = 7;
         ArrayResize(period_labels, periods_count);
         period_labels[0] = "Пн";
         period_labels[1] = "Вт";
         period_labels[2] = "Ср";
         period_labels[3] = "Чт";
         period_labels[4] = "Пт";
         period_labels[5] = "Сб";
         period_labels[6] = "Вс";
         IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по дням недели");
         break;
         
      case SEASONALITY_HOURS:
         periods_count = 24;
         ArrayResize(period_labels, periods_count);
         for(int i = 0; i < periods_count; i++)
            period_labels[i] = IntegerToString(i) + ":00";
         IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по часам");
         break;
   }
   
   ArrayResize(seasonality_data, periods_count);
   ArrayInitialize(seasonality_data, 0.0);
   
   IndicatorSetInteger(INDICATOR_DIGITS, ShowPercentage ? 2 : _Digits);
   
   return(INIT_SUCCEEDED);
}

Essa função estabelece a base do funcionamento do indicador. Por exemplo, para os dias da semana é criado um array de rótulos de "Seg" até "Dom", enquanto para as horas é criado de "0:00" até "23:00". O nome do indicador também é alterado para que o usuário entenda imediatamente qual tipo de sazonalidade foi selecionado. Isso torna o indicador intuitivo.


Cálculo da sazonalidade

O trabalho principal do indicador ocorre na função OnCalculate. Ela analisa os dados históricos de preços, calcula a rentabilidade média para cada período e preenche os buffers para renderização. Primeiro é verificado se há barras suficientes no gráfico. Em seguida são criados arrays auxiliares para somar a rentabilidade e contar o número de barras em cada período.

Para cada barra é calculada a rentabilidade como a razão entre a diferença dos preços de fechamento e o preço de fechamento atual. A função GetPeriodIndex determina a qual período a barra pertence, e os dados são registrados nos arrays. Depois disso é calculada a rentabilidade média, que pode ser convertida em porcentagem ou zerada no caso de valores negativos. Os buffers são então preenchidos com os dados, e para a previsão é chamada uma função separada.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < BarsToAnalyze + 1)
      return(0);

   ArrayInitialize(seasonality_data, 0.0);
   
   double period_returns[];
   int period_counts[];
   ArrayResize(period_returns, periods_count);
   ArrayResize(period_counts, periods_count);
   ArrayInitialize(period_returns, 0.0);
   ArrayInitialize(period_counts, 0);
   
   int start_pos = MathMax(0, rates_total - BarsToAnalyze - 1);
   
   for(int i = start_pos; i < rates_total - 1; i++)
   {
      double return_value = 0.0;
      if(close[i] != 0)
         return_value = (close[i+1] - close[i]) / close[i];
      
      int period_index = GetPeriodIndex(time[i]);
      
      if(period_index >= 0 && period_index < periods_count)
      {
         period_returns[period_index] += return_value;
         period_counts[period_index]++;
      }
   }
   
   for(int i = 0; i < periods_count; i++)
   {
      if(period_counts[i] > 0)
      {
         seasonality_data[i] = period_returns[i] / period_counts[i];
         if(ShowPercentage)
            seasonality_data[i] *= 100.0;
         if(ShowPositiveOnly && seasonality_data[i] < 0)
            seasonality_data[i] = 0.0;
      }
   }
   
   for(int i = 0; i < rates_total; i++)
   {
      int period_index = GetPeriodIndex(time[i]);
      if(period_index >= 0 && period_index < periods_count)
      {
         SeasonalityBuffer[i] = seasonality_data[period_index];
         ForecastLineBuffer[i] = seasonality_data[period_index];
      }
      else
      {
         SeasonalityBuffer[i] = 0.0;
         ForecastLineBuffer[i] = 0.0;
      }
      ForecastPointsBuffer[i] = EMPTY_VALUE;
   }
   
   if(ShowForecastOnChart)
      DrawForecastOnChart(rates_total, time);
   
   return(rates_total);
}

Esse código é o coração do indicador. Ele transforma dados brutos de preços em padrões compreensíveis. Imagine que você esteja analisando o gráfico diário de um par de moedas. Para cada dia da semana o indicador calcula quanto o preço mudou e encontra o valor médio. Se você observar que às sextas-feiras o mercado cai em média 0.05%, isso pode servir como sinal para uma estratégia de trading.


Determinação do período

Para entender a qual período uma barra pertence, criamos a função GetPeriodIndex. Ela converte o tempo da barra em um índice de período dependendo do tipo de sazonalidade. Para os dias do mês ela obtém o número do dia e subtrai uma unidade para gerar um índice de 0 a 30. Para os dias da semana ela converte o valor do dia da semana, onde domingo é 0 e segunda-feira é 1, em uma ordem onde segunda-feira é 0 e domingo é 6. Para as horas simplesmente retorna o valor da hora de 0 a 23.

int GetPeriodIndex(datetime bar_time)
{
   MqlDateTime dt;
   TimeToStruct(bar_time, dt);
   
   switch(SeasonalityType)
   {
      case SEASONALITY_DAYS_OF_MONTH:
         return(dt.day - 1);
         
      case SEASONALITY_DAYS_OF_WEEK:
         return(dt.day_of_week == 0 ? 6 : dt.day_of_week - 1);
         
      case SEASONALITY_HOURS:
         return(dt.hour);
   }
   
   return(-1);
}

Essa função funciona como uma bússola que ajuda o indicador a entender em qual "compartimento" os dados da barra devem ser colocados. Sem ela não seria possível associar o preço a um dia ou hora específicos.


Renderização da previsão

A previsão é o que torna o indicador não apenas analítico, mas também útil na prática. A função DrawForecastOnChart desenha os valores esperados de sazonalidade nas duas últimas barras do gráfico. Ela identifica o período atual e os dois seguintes utilizando a função GetNextPeriod. Os valores de sazonalidade desses períodos são registrados nos buffers da linha e dos pontos de previsão. Para que a linha de previsão pareça suave, o valor do período atual é registrado no buffer correspondente à penúltima barra.

void DrawForecastOnChart(int rates_total, const datetime &time[])
{
   if(rates_total < 3)
      return;
   
   datetime current_time = time[rates_total - 1];
   int current_period = GetPeriodIndex(current_time);
   int next_period1 = GetNextPeriod(current_period);
   int next_period2 = GetNextPeriod(next_period1);
   
   int forecast_pos1 = rates_total - 2;
   int forecast_pos2 = rates_total - 1;
   
   if(next_period1 >= 0 && next_period1 < periods_count)
   {
      ForecastLineBuffer[forecast_pos1] = seasonality_data[next_period1];
      ForecastPointsBuffer[forecast_pos1] = seasonality_data[next_period1];
   }
   
   if(next_period2 >= 0 && next_period2 < periods_count)
   {
      ForecastLineBuffer[forecast_pos2] = seasonality_data[next_period2];
      ForecastPointsBuffer[forecast_pos2] = seasonality_data[next_period2];
   }
   
   if(rates_total >= 3)
   {
      int current_pos = rates_total - 3;
      int current_period_idx = GetPeriodIndex(time[current_pos]);
      
      if(current_period_idx >= 0 && current_period_idx < periods_count)
      {
         double current_value = seasonality_data[current_period_idx];
         double next_value = (next_period1 >= 0) ? seasonality_data[next_period1] : current_value;
         
         ForecastLineBuffer[current_pos] = current_value;
         if(forecast_pos1 < rates_total)
            ForecastLineBuffer[forecast_pos1] = next_value;
      }
   }
}

A previsão se assemelha a um olhar para o futuro: o indicador informa o que esperar com base em dados históricos. Se, por exemplo, as terças-feiras geralmente apresentam uma rentabilidade de +0.07%, o indicador mostrará isso no gráfico, ajudando o trader a se preparar.


Cálculo do próximo período

A função GetNextPeriod determina qual período vem após o atual. Ela utiliza aritmética modular para garantir uma transição cíclica: após o dia 31 do mês vem o dia 1, após domingo vem segunda-feira e após 23:00 vem 0:00.

int GetNextPeriod(int current_period)
{
   if(current_period < 0)
      return(-1);
   
   switch(SeasonalityType)
   {
      case SEASONALITY_DAYS_OF_MONTH:
         return((current_period + 1) % 31);
         
      case SEASONALITY_DAYS_OF_WEEK:
         return((current_period + 1) % 7);
         
      case SEASONALITY_HOURS:
         return((current_period + 1)rate_total % 24);
   }
   
   return(-1);
}

Essa função garante que a previsão sempre observe os períodos futuros corretos, preservando a lógica do tempo cíclico. Ela atua como um calendário preciso do mercado, assegurando que o indicador faça a transição correta de um período para o seguinte, seja na mudança dos dias do mês, dos dias da semana ou das horas. Imagine que o mercado seja um enorme relógio em que cada tique marca não apenas o tempo, mas também possíveis movimentos de preço. Após o dia 31 do mês a função retorna suavemente ao dia 1, após domingo passa para segunda-feira e após 23:00 retorna para 0:00. Essa abordagem cíclica reflete a estrutura real do tempo nos mercados financeiros, permitindo que o indicador antecipe quais períodos podem ser relevantes para o trader.


Exibição das estatísticas

Para que o trader compreenda rapidamente os resultados da análise, o indicador exibe estatísticas detalhadas diretamente no gráfico. A função ShowStatistics cria uma mensagem de texto que, como um narrador, transforma números secos em uma história interessante sobre os padrões do mercado.

Ela começa com um título que muda conforme o tipo de sazonalidade selecionado, seja análise por dias do mês, dias da semana ou horas do dia. Em seguida é indicado o número de barras analisadas, o que dá ao trader uma noção da profundidade dos dados históricos utilizados nos cálculos. Depois é adicionada a previsão obtida pela função GetForecast, que estima como o mercado pode se comportar nos próximos dois períodos com base nas tendências históricas. Em seguida a função examina cuidadosamente o array seasonality_data, encontrando os períodos com a maior e a menor rentabilidade. Esses períodos recebem a classificação de melhores e piores, e seus valores são exibidos com precisão de três casas decimais para que o trader possa avaliar sua relevância.

A mensagem termina com estatísticas completas, nas quais para cada período é indicado seu rótulo, por exemplo "Seg" ou "15", e a rentabilidade média. Esse texto aparece no canto superior direito do gráfico, como um relatório de um pesquisador que revela os ritmos do mercado e os torna claros e úteis para a tomada de decisões de trading.


Considerações finais

A sazonalidade nos mercados financeiros não é misticismo, mas uma regularidade fundamentada matematicamente que pode se tornar uma ferramenta poderosa no arsenal de um trader. O indicador que criamos transforma movimentos caóticos de preços em padrões compreensíveis, revelando ritmos ocultos do mercado por dias do mês, dias da semana ou horas da sessão de negociação.

Graças à configuração flexível de parâmetros, à exibição visual dos resultados na forma de um histograma com previsão e às estatísticas detalhadas, esta ferramenta permite não apenas analisar dados históricos, mas também construir suposições fundamentadas sobre movimentos futuros do preço. A integração de elementos de previsão torna o indicador aplicável na prática para desenvolver estratégias de trading.

O código do indicador é fácil de adaptar para diferentes ativos e timeframes, e a explicação passo a passo de cada função dá a possibilidade de modificar o algoritmo de acordo com as próprias ideias de trading. Em última instância, este indicador é uma ponte entre cálculos estatísticos complexos e a tomada de decisões de trading concretas, transformando dados em conhecimento e conhecimento em lucro potencial.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/18672

Arquivos anexados |
Seasonaly_Ind.mq5 (31.38 KB)
Últimos Comentários | Ir para discussão (1)
Ivan Finogenov
Ivan Finogenov | 15 jan. 2026 em 13:01
Boa tarde! É uma lógica interessante e uma abordagem correta de negociação, mas tenho uma pergunta. Yevgeniy Koshtenko Se considerarmos o cálculo de dias em um mês, por que ele cria dados diferentes se mudarmos o TF H1 para H4 ou mesmo D1? Idealmente, o cálculo não deveria mudar com a alteração do TF. O cálculo muda tanto nos dados de texto quanto no histograma.
Análise de lacunas tempo&lt;rais de preço em MQL5 (Parte II): Criamos um mapa de calor da distribuição de liquidez no tempo Análise de lacunas tempo&lt;rais de preço em MQL5 (Parte II): Criamos um mapa de calor da distribuição de liquidez no tempo
Guia detalhado para criar um indicador de mapa de calor para MetaTrader 5 que visualiza a distribuição temporal do preço na forma de um mapa de calor. O artigo revela a base matemática da análise da densidade temporal, na qual cada nível de preço é colorido do vermelho (tempo mínimo de permanência) ao azul (tempo máximo de permanência).
Operando com o Calendário Econômico do MQL5 (Parte 6): Automatizando a Entrada de Trades com Análise de Eventos de Notícias e Temporizadores de Contagem Regressiva Operando com o Calendário Econômico do MQL5 (Parte 6): Automatizando a Entrada de Trades com Análise de Eventos de Notícias e Temporizadores de Contagem Regressiva
Neste artigo, implementamos a entrada automática de trades utilizando o Calendário Econômico do MQL5, aplicando filtros definidos pelo usuário e deslocamentos de tempo para identificar eventos de notícias qualificados. Comparamos os valores de previsão e valores anteriores para determinar se devemos abrir uma operação BUY ou SELL. Temporizadores dinâmicos de contagem regressiva exibem o tempo restante até a divulgação da notícia e são redefinidos automaticamente após a execução de um trade.
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.
Redes neurais em trading: Segmentação periódica adaptativa (Criação de tokens) Redes neurais em trading: Segmentação periódica adaptativa (Criação de tokens)
Propomos que você embarque em uma jornada fascinante pelo mundo da análise adaptativa de séries temporais financeiras e descubra como transformar uma complexa análise espectral e uma convolução flexível em sinais reais de trading. Você verá como o LightGTS escuta o ritmo do mercado, ajustando-se às suas mudanças por meio de um passo de janela variável, e como a aceleração com OpenCL permite transformar cálculos no caminho mais curto para decisões lucrativas.