Русский
preview
Redes neurais no trading: abordagem semântica baseada em spikes para identificação espaçotemporal (Conclusão)

Redes neurais no trading: abordagem semântica baseada em spikes para identificação espaçotemporal (Conclusão)

MetaTrader 5Sistemas de negociação |
26 0
Dmitriy Gizlyk
Dmitriy Gizlyk

Introdução

Quando o mercado fica em silêncio, isso não significa que esteja tranquilo. Sob a superfície, a atividade não para: rajadas de ordens, oscilações na liquidez, variações de volume. Tudo isso se manifesta em impulsos curtos, quase imperceptíveis, dos quais nasce o movimento do preço. Cada tick é um sinal no tecido vivo do mercado. E é justamente nesses instantes, quando a maioria ainda percebe apenas silêncio, que uma tendência realmente começa a se formar.

Os métodos clássicos de análise têm inércia demais para captar esse momento. Eles enxergam um fato já consumado: um candle, um extremo, uma formação. Mas, para o trader, o mais importante é perceber a mudança antes que ela se torne evidente. É aí que entram em cena os modelos de nova geração, arquiteturas capazes de reconhecer eventos.

Uma dessas arquiteturas é a S3CE-Net, Spike-guided Spatiotemporal Semantic Coupling and Expansion Network. Originalmente, ela foi proposta para processar fluxos de dados de câmeras de eventos, nas quais cada pixel registra não uma imagem estática, mas uma variação instantânea de luminosidade, uma espécie de spike, um impulso. Em essência, são os mesmos tipos de eventos que encontramos no mercado: irregulares, assíncronos, breves, mas relevantes.

Foi então que pensamos: afinal, o mercado financeiro também é um sistema baseado em eventos. Aqui, o tempo não flui de maneira uniforme. A dinâmica é marcada por rajadas de atividade, momentos em que um grande participante surge repentinamente no fluxo de ordens, a liquidez se contrai e o preço acelera. Se ensinarmos o modelo a interpretar esses impulsos como uma estrutura, ele poderá antecipar mudanças e reagir a elas.

Em trabalhos anteriores, já nos familiarizamos com a fundamentação teórica do framework S3CE-Net. Analisamos a ideia da abordagem baseada em eventos, explicamos por que o mercado pode ser interpretado como um fluxo de impulsos e estabelecemos as bases para transferir as abordagens propostas para o ambiente MQL5.

A etapa seguinte representou um avanço de engenharia: implementamos o módulo SSAM (Spike-guided Spatial-Temporal Attention Mechanism) e o adaptamos às características computacionais do terminal de negociação.

Agora chegou a hora da etapa final. Aqui, reunimos tudo, da concepção à prática, da teoria dos eventos à avaliação dos resultados. Vamos verificar como a S3CE-Net se comporta com dados históricos reais, quão robusta é ao ruído de mercado e se seu mecanismo de atenção consegue distinguir sinais reais em meio a oscilações aleatórias.

A arquitetura da S3CE-Net baseia-se em duas ideias que se complementam. A primeira é o SSAM, um mecanismo de atenção que aprende a se concentrar em eventos importantes e a suprimir os irrelevantes. Ele funciona como um trader experiente, capaz de perceber, por trás do fluxo de padrões de candles, o sentimento do mercado: onde um impulso ainda concentra energia e onde resta apenas uma onda que perde força. A segunda é o STFS (Spatiotemporal Feature Sampling Strategy). Ele amplia a percepção do modelo, permitindo que acompanhe uma sequência fluida de eventos interligados.

Fazendo uma analogia, o SSAM representa a atenção do trader, enquanto o STFS funciona como sua memória. Juntos, eles formam um modelo capaz de perceber e lembrar o que aconteceu anteriormente. Essa é a verdadeira essência da análise: não ficar preso ao momento, sem perder a conexão com o contexto.

Quando adaptamos essas ideias ao ambiente MQL5, a tarefa deixa de ser apenas técnica e ganha também uma dimensão filosófica. Uma câmera de vídeo registra variações de luminosidade, enquanto no mercado observamos mudanças no estado do preço e do volume. Cada tick, cada pico de volatilidade, cada mudança no livro de ofertas pode se tornar um spike, desde que o critério de relevância seja definido corretamente.

Essa abordagem nos leva a enxergar o mercado de outra maneira. Vemos um sistema de impulsos a partir dos quais se forma um movimento de maior escala. A S3CE-Net parece traduzir a linguagem do mercado para a linguagem dos eventos, em que cada mudança é uma letra e toda a sequência forma uma frase. O modelo aprende a interpretar essa linguagem como um analista experiente interpreta a fita de negócios: não com os olhos, mas com a intuição. A S3CE-Net não espera a confirmação de um padrão; ela reage aos sinais precursores. Isso é especialmente importante em condições de alta volatilidade, nas quais os indicadores clássicos reagem com atraso.

Para um trader acostumado a se basear em padrões de candles ou indicadores, essa ideia pode parecer incomum. Mas, observando com mais atenção, trata-se de uma continuação lógica da evolução da análise. Primeiro, avaliávamos níveis e zonas; depois, passávamos aos modelos probabilísticos; agora chegou a vez dos modelos baseados em eventos. É um passo natural, da observação à compreensão, dos dados ao significado.

É justamente por isso que a implementação da S3CE-Net em MQL5 não se limita a objetivos puramente técnicos. Queremos mostrar como princípios arquitetônicos desenvolvidos para uma área completamente diferente podem dar novo fôlego à análise de mercado. Afinal, tanto o trading quanto a análise baseada em eventos buscam estrutura no caos e tentam extrair significado de um fluxo de informações.

O mercado, como qualquer sistema vivo, respira de forma irregular, e quem consegue perceber esse ritmo ganha vantagem. A S3CE-Net é uma tentativa de dotar a máquina dessa capacidade de escuta, permitindo ao algoritmo perceber onde o movimento começa e onde termina. Não há magia nisso: é consequência de uma análise cuidadosa da estrutura dos eventos.

A visualização autoral do framework S3CE-Net é apresentada a seguir.

altVisualização autoral do framework S3CE-Net

Hoje chegamos ao desfecho natural. Ficaram para trás a arquitetura, a implementação e a adaptação. À frente estão os testes, a avaliação e, talvez, uma reavaliação da abordagem tradicional de previsão. Vamos verificar como o modelo desenvolvido lida com fluxos reais do mercado, com que precisão distingue sinais verdadeiros e como seu mecanismo de atenção baseado em spikes afeta a qualidade das previsões.


Módulo STFS

Hoje, resta-nos dar o passo final na implementação das abordagens propostas pelos autores do framework S3CE-Net: construir o módulo STFS (Spatio-Temporal Feature Sampling Strategy). Esse é o elemento final de toda a arquitetura, capaz de ensinar o modelo a enxergar o mercado de forma holística: perceber um contexto mais amplo, em vez de se fixar em padrões locais.

Assim como um trader experiente, o STFS aprende a distinguir o essencial do secundário. Sua função é fazer com que o modelo analise não apenas fragmentos específicos dos dados de mercado, mas também as relações entre eles, combinando as dimensões temporal e espacial. Afinal, o mercado não é apenas uma linha em um gráfico. É um sistema multidimensional, no qual cada candle, cada volume e cada impulso estão interligados por fios invisíveis. É justamente essa rede de relações que o algoritmo precisa aprender a reconhecer.

Para isso, os autores da S3CE-Net propuseram um mecanismo bastante elegante. Durante o treinamento, o módulo divide o mapa de características em subamostras temporais e espaciais.

Primeiro, realizamos a amostragem temporal. Um conjunto de passos temporais é selecionado aleatoriamente a partir da sequência completa. Para cada um deles, calcula-se uma representação média, uma espécie de fatia do estado do mercado em um momento específico. Essas fatias formam um conjunto de características temporais que refletem a dinâmica do processo.

Em seguida, vem a amostragem espacial. O mapa de características é dividido em várias regiões aleatórias. A média de cada região é calculada separadamente, o que permite identificar estruturas locais. Fazendo uma analogia com o mercado, isso se assemelha à análise de diferentes setores da atividade de mercado.

Por fim, na última etapa, é extraído um vetor semântico global, que fornece uma representação generalizada de todo o estado do mercado. Ele é obtido pela média de todo o mapa de características e atua como um indicador agregado, sintetizando tanto os padrões temporais quanto os espaciais.

O treinamento do modelo baseia-se em duas funções de perda fundamentais: triplet loss e entropia cruzada com suavização de rótulos. A primeira ajuda o modelo a destacar as principais características, acentuando as diferenças entre estados de mercado realmente relevantes. A segunda estabiliza o treinamento, evitando a supervalorização de oscilações aleatórias. Em conjunto, elas conferem ao sistema uma intuição, permitindo que ele não se perca em meio ao ruído de mercado.

Neste trabalho, vamos repensar ligeiramente a abordagem original proposta pelos autores da S3CE-Net. Na concepção original, o foco principal estava na análise da estrutura espaçotemporal da imagem. No caso dos dados financeiros, porém, a natureza do sinal analisado é diferente. O mercado não possui geometria: sua estrutura não é visual, mas semântica. Aqui, as características formam um vetor de fatores econômicos, no qual cada coordenada carrega seu próprio contexto: preço, volume, volatilidade, indicadores de sentimento e métricas estruturais de liquidez. E, o que é especialmente importante, essas relações não precisam guardar correspondência com a proximidade dos índices nesse vetor.

É exatamente por isso que a aplicação direta da amostragem espacial, como na implementação clássica do STFS para imagens, nem sempre se justifica. Não podemos presumir que elementos adjacentes no vetor de características tenham significado ou influência semelhantes. Nos dados de mercado, a proximidade entre os atributos é de natureza lógico-funcional. O preço pode estar intimamente relacionado ao volume, mas não necessariamente a um indicador técnico posicionado ao lado no array de dados.

Por esse motivo, utilizamos uma estratégia de amostragem reformulada, semelhante à amostragem temporal, mas aplicada ao eixo espacial, ou seja, ao eixo das características. Em outras palavras, assim como na amostragem temporal selecionamos pontos temporais aleatórios, aqui também selecionaremos aleatoriamente um subconjunto de características, formando uma amostra para o treinamento do modelo. Essa abordagem preserva os princípios de aleatoriedade e generalização, mas os adapta especificamente aos dados vetoriais de mercado, nos quais o que importa é o significado, e não a posição.

Além disso, diferentemente da metodologia original, na qual os dados que não entram na amostra são simplesmente descartados, adotaremos outra abordagem. Nenhuma característica será perdida. Com os dados restantes, formaremos amostras alternativas, que serão utilizadas como contextos auxiliares. Isso não apenas aumenta a eficiência do treinamento, mas também melhora a robustez do modelo: ele aprende a enxergar o mercado sob diferentes perspectivas, como se o analisasse por meio de um conjunto de observações independentes, porém coerentes.

Como resultado, são formadas cinco representações diferentes dos dados analisados:

  • uma representação completa, ou global, que reflete o panorama geral do mercado;
  • 2 amostras, uma temporal e outra por características, que permitem ao modelo manter o foco em informações locais;
  • 2 amostras alternativas, que servem como fontes adicionais para o treinamento contextual.

Também modificamos a forma de gerar a previsão final. Na implementação clássica do STFS para imagens, calcula-se a média das características dos blocos selecionados e, em seguida, forma-se um único vetor. Para dados de mercado, essa abordagem pode suavizar sinais importantes e fazer com que impulsos sutis, mas relevantes, se percam.

Por isso, adotamos uma abordagem diferente: para cada uma das amostras obtidas, é gerada uma previsão separada. Em outras palavras, o modelo não combina previamente as informações, mas analisa cada perspectiva individualmente. Isso permite avaliar os sinais em diferentes contextos, sem perder a relevância local dos dados e mantendo a capacidade de distinguir tendências fracas, mas potencialmente importantes.

Essa abordagem se aproxima do estilo analítico de um trader profissional. Um especialista experiente nunca se baseia em um único indicador nem em um único intervalo de tempo: ele elabora vários cenários simultaneamente, compara-os entre si e, com base nisso, decide que posição assumir. Da mesma forma, nosso modelo aprende a gerar várias previsões a partir das mesmas informações, analisando o mercado sob diferentes perspectivas.

Agora que definimos e refinamos a forma de construir o módulo STFS, chegou a hora de passar à implementação prática. Como primeiro passo, formamos 5 representações dos dados analisados. Para criar todas as amostras de forma eficiente e em paralelo, utilizamos o contexto OpenCL, permitindo processar arrays de dados simultaneamente e acelerar significativamente os cálculos.

A implementação se baseia no kernel STFS, que cria subamostras com base em máscaras aleatórias de passos temporais e características. Verifica-se se cada ponto de dados é válido e, em seguida, são formadas todas as combinações de amostras necessárias. Essa abordagem permite criar simultaneamente tanto a amostra principal quanto as alternativas, sem depender de processamento sequencial, o que é essencial para processar dados de mercado em fluxo contínuo.

__kernel void STFS(__global const float* inputs,
                   __global const float* mask_time,
                   __global const float* mask_spatial,
                   __global float* outputs
                  )
  {
   const size_t time_id = get_global_id(0);
   const size_t spat_id = get_global_id(1);
   const size_t head = get_local_id(2);
   const size_t total_times = get_global_size(0);
   const size_t total_spats = get_global_size(1);
   const size_t total_heads = get_local_size(2);
//---
   __local float temp[3];

Para cada posição no fluxo de dados, definimos identificadores locais e globais para o tempo, o eixo espacial e as cabeças (heads), responsáveis pelos diferentes tipos de amostras.

Na primeira etapa, são determinados os offsets até os elementos correspondentes nos buffers de dados lineares.

   const int shift_in = RCtoFlat(time_id, spat_id, total_times, total_spats, 1);
   const int shift_out = RCtoFlat(time_id, spat_id, total_times, total_spats, head);

Em seguida, os valores necessários são extraídos dos buffers globais de dados, incluindo as máscaras temporais e espaciais usadas para filtrar os dados.

Para minimizar o número de operações dispendiosas de acesso à memória global, distribuímos essas operações entre as threads do grupo de trabalho e armazenamos os valores em um array na memória local. Dessa forma, cada valor é extraído do buffer global apenas uma vez.

   switch(head)
     {
      case 0:
         temp[0] = IsNaNOrInf(inputs[shift_in], 0);
         break;
      case 1:
         temp[1] = IsNaNOrInf(mask_time[time_id], 0);
         break;
      case 2:
         temp[2] = IsNaNOrInf(mask_spatial[spat_id], 0);
         break;
     }
   BarrierLoc

Em seguida, o algoritmo cria subamostras separadas. A primeira, para a amostra temporal principal; a segunda, para a amostra temporal alternativa; a terceira, para a amostra de características; e a quarta, para o conjunto alternativo de características.

   float out = temp[0];
   if(out != 0)
      switch(head)
        {
         case 1:
            out *= temp[1];
            break;
         case 2:
            out *= (1 - temp[1]);
            break;
         case 3:
            out *= temp[2];
            break;
         case 4:
            out *= (1 - temp[2]);
            break;
        }
//---
   outputs[shift_out] = IsNaNOrInf(out, 0);
  }

Cada uma dessas subamostras é armazenada em seu próprio bloco do array de resultados para posterior processamento pelo modelo.

Assim, a execução do kernel produz um conjunto completo de cinco representações dos dados originais, prontas para a posterior geração de previsões individuais. Esse é um ponto-chave da implementação prática do STFS: o modelo passa a aprender simultaneamente com diferentes perspectivas do mercado, analisando-o de forma global, local e também por meio de representações alternativas.

Vale destacar que o algoritmo do kernel responsável pela distribuição dos gradientes de erro segue uma lógica semelhante. Deixaremos os detalhes da implementação desse mecanismo para estudo por conta própria. O código completo do programa OpenCL está disponível no anexo.

A próxima etapa é construir o módulo STFS no programa principal, integrando todas as ideias de amostragem e geração de previsões em uma única arquitetura. Para isso, é criado um novo objeto CNeuronSTFS, que herda de CNeuronBaseOCL as funcionalidades básicas de um neurônio com suporte a OpenCL.

class CNeuronSTFS    :  public   CNeuronBaseOCL
  {
protected:
   CNeuronBaseOCL    cMaskTime;
   CNeuronBaseOCL    cMaskSpatial;
   CNeuronBaseOCL    cConcatenated;
   CNeuronConvOCL    cSpatialDScale;
   CNeuronConvOCL    cTimeDScale;
   CNeuronTransposeVRCOCL caTranspose[2];
   CBufferFloat      cOne;
   //---
   virtual bool      STFS(CNeuronBaseOCL *NeuronOCL);
   virtual bool      STFSGrad(CNeuronBaseOCL *NeuronOCL);
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronSTFS(void) {};
                    ~CNeuronSTFS(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint chanels_in, uint chanels_out, uint units_in, uint units_out,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void)   override const   {  return defNeuronSTFS;   }
   //--- methods for working with files
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   virtual void      SetActivationFunction(ENUM_ACTIVATION value) override;
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
  };

A estrutura da classe reflete os principais componentes do STFS: objetos separados para as máscaras temporais e de características (cMaskTime, cMaskSpatial), o tensor de concatenação das representações (cConcatenated) e camadas convolucionais e de transposição especializadas (cSpatialDScale, cTimeDScale, caTranspose[2]), que permitem projetar corretamente os dados espaçotemporais. Em conjunto, esses elementos formam a base sobre a qual o modelo pode considerar simultaneamente diferentes aspectos das informações de mercado e gerar cinco variantes dos valores previstos a partir de diferentes representações.

Todos os componentes internos da classe CNeuronSTFS são declarados como estáticos, o que permite manter o construtor e o destrutor vazios. O objeto é criado e totalmente preparado no método Init. Essa abordagem simplifica o gerenciamento de memória e permite uma inicialização previsível de todos os componentes do módulo.

bool CNeuronSTFS::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                       uint chanels_in, uint chanels_out, uint units_in, uint units_out,
                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, chanels_out * units_out, optimization_type, batch))
      return false;
   activation = None;

O método Init configura sequencialmente cada elemento interno do módulo STFS. Primeiro, é executada a inicialização básica da classe pai e, em seguida, o objeto principal é configurado sem função de ativação.

É especialmente importante destacar que a ausência de uma função de ativação no nível do módulo é uma condição obrigatória para o funcionamento correto do algoritmo. Os autores do framework S3CE-Net propõem utilizar o módulo STFS apenas na fase de treinamento. No modo de produção, quando o sistema gera previsões em tempo real, utiliza-se somente a representação completa dos dados, sem criar subamostras. Por isso, como veremos adiante, a saída do módulo retorna apenas a previsão obtida a partir da representação completa, enquanto todas as subamostras intermediárias servem exclusivamente para aprimorar o treinamento e formar um modelo mais robusto e preciso. Dentro do módulo, porém, o treinamento com as subamostras é executado em paralelo e, para propagar corretamente os gradientes entre todos os componentes, é necessário utilizar o erro sem ajustes decorrentes da derivada da função de ativação.

Ao mesmo tempo, para manter a flexibilidade de acordo com as necessidades do usuário, a função de ativação ainda pode ser utilizada, mas passa a ser aplicada no último objeto de projeção dos dados. É justamente nesse ponto, ao obter a previsão final, que podemos aplicar uma transformação não linear, caso isso seja necessário para um cenário específico de funcionamento do modelo.

Mas voltemos ao algoritmo de inicialização. Em seguida, são criadas e configuradas as máscaras para a amostragem temporal e por características. Cada uma recebe seu próprio índice.

   int index = 0;
   if(!cMaskTime.Init(0, index, OpenCL, units_in, optimization, iBatch))
      return false;
   cMaskTime.SetActivationFunction(None);
   index++;
   if(!cMaskSpatial.Init(0, index, OpenCL, chanels_in, optimization, iBatch))
      return false;
   cMaskSpatial.SetActivationFunction(None);

O objeto de concatenação das representações é formado considerando todas as características e o tamanho da sequência, multiplicados pelo número de amostras, criando a base para a previsão subsequente.

   index++;
   if(!cConcatenated.Init(0, index, OpenCL, chanels_in * units_in * 5, optimization, iBatch))
      return false;
   cConcatenated.SetActivationFunction(None);

Depois disso, é inicializado o bloco de projeção, composto pelas camadas convolucionais responsáveis pela projeção dos componentes espaciais e temporais (cSpatialDScale, cTimeDScale). A essas camadas são atribuídas as funções de ativação correspondentes, necessárias para dimensionar corretamente os sinais de saída.

   if(!cSpatialDScale.Init(0, index, OpenCL, chanels_in, chanels_in, chanels_out, units_in, 5, optimization, iBatch))
      return false;
   cSpatialDScale.SetActivationFunction(SoftPlus);
   index++;
   if(!cTimeDScale.Init(0, index, OpenCL, units_in, units_in, units_out, chanels_out, 5, optimization, iBatch))
      return false;
   cTimeDScale.SetActivationFunction(TANH);

A inicialização é concluída pelos objetos de transposição, que permitem converter os dados de forma consistente entre as diferentes representações.

   index++;
   if(!caTranspose[0].Init(0, index, OpenCL, 5, units_in, chanels_out, optimization, iBatch))
      return false;
   if(!caTranspose[1].Init(0, index, OpenCL, 5, chanels_out, units_out, optimization, iBatch))
      return false;

O buffer cOne é utilizado apenas no algoritmo de distribuição dos gradientes de erro e permite obter cópias dos dados para todas as projeções.

   if(!cOne.BufferInit(5, 1) ||
      !cOne.BufferCreate(OpenCL))
      return false;
//---
   return true;
  }

Todos os elementos são configurados para executar cálculos paralelos no contexto OpenCL, garantindo alto desempenho no processamento de arrays de dados de mercado.

O algoritmo de propagação para frente está implementado no método feedForward. Sua função é propagar os dados, etapa por etapa, por todos os componentes do módulo até obter uma representação pronta para a previsão.

bool CNeuronSTFS::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!STFS(NeuronOCL))
      return false;

Primeiro, é chamado o método STFS, que cria cinco representações dos dados: uma completa, duas amostras e duas amostras alternativas. Diferentemente dos métodos usuais de enfileiramento de kernels para execução, o método STFS primeiro amostra as máscaras dos passos temporais e das características analisadas. Somente após essa preparação o controle é transferido para o kernel correspondente.

Em seguida, os dados passam pela camada convolucional de projeção das características espaciais, permitindo que o modelo identifique estruturas locais e relações entre as características nas diferentes representações.

   if(!cSpatialDScale.FeedForward(cConcatenated.AsObject()))
      return false;

Depois disso, o tensor é transformado, permitindo a conversão correta entre as representações espacial e temporal.

   if(!caTranspose[0].FeedForward(cSpatialDScale.AsObject()))
      return false;

Na sequência, a camada convolucional de projeção temporal processa os dados ao longo do eixo temporal, captando a dinâmica do mercado e identificando padrões temporais relevantes.

   if(!cTimeDScale.FeedForward(caTranspose[0].AsObject()))
      return false;

A segunda camada de transposição retorna os dados à representação original.

   if(!caTranspose[1].FeedForward(cTimeDScale.AsObject()))
      return false;

Na etapa final, selecionamos apenas os dados da representação global, conforme propõem os autores do framework para o modo de produção.

   if(!DeConcat(Output, caTranspose[1].getPrevOutput(), caTranspose[1].getOutput(),
                               Neurons(), caTranspose[1].Neurons() - Neurons(), 1))
      return false;
//---
   return true;
  }

Assim, o método feedForward mantém um fluxo fluido de dados por todo o módulo STFS, criando a base para os cálculos subsequentes do erro, a atualização dos pesos e a geração da previsão final a partir da representação completa dos dados.

Depois que o modelo gera os valores previstos, inicia-se a retropropagação dos gradientes do erro. O método calcInputGradients é responsável por distribuir o erro entre todos os componentes do módulo STFS de acordo com a influência de cada um no resultado final.

bool CNeuronSTFS::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;

É importante destacar que o módulo STFS pode ser utilizado tanto na saída do modelo quanto no interior de uma arquitetura mais complexa. Nesses casos, na entrada da retropropagação não recebemos os valores efetivos, mas o erro propagado pelos objetos subsequentes da rede.

As diferentes projeções dos dados, incluindo a global, a temporal, a de características e as amostras alternativas, apresentam erros distintos. Para treinar corretamente cada projeção, é preferível dispor dos valores-alvo, e não dos desvios. Para obter esses valores-alvo, somamos os valores previstos pelo módulo ao erro recebido, formando um tensor que permite treinar corretamente cada uma das projeções.

É exatamente por isso que a ausência de uma função de ativação na saída do módulo é tão importante. Qualquer transformação não linear distorceria esse mecanismo, impedindo que a rede distribuísse corretamente os erros e formasse valores-alvo precisos para cada projeção.

   if(!SumAndNormilize(Output, Gradient, PrevOutput, 1, false, 0, 0, 0, 1))
      return false;
   if(!MatMul(GetPointer(cOne), PrevOutput, caTranspose[1].getGradient(), cOne.Total(), 1, Neurons(), 1, false))
      return false;

Multiplicamos o tensor de valores-alvo obtido por um vetor de uns, produzindo o número necessário de cópias. Em seguida, determinamos o erro de cada uma das representações.

   float error = 1;
   if(!caTranspose[1].calcOutputGradients(caTranspose[1].getGradient(), error))
      return false;

Depois disso, os gradientes são propagados pelo bloco de projeção de dados até o tensor de concatenação das representações, considerando corretamente a contribuição de cada elemento para a previsão geral.

   if(!cTimeDScale.CalcHiddenGradients(caTranspose[1].AsObject()))
      return false;
   if(!caTranspose[0].CalcHiddenGradients(cTimeDScale.AsObject()))
      return false;
   if(!cSpatialDScale.CalcHiddenGradients(caTranspose[0].AsObject()))
      return false;
   if(!cConcatenated.CalcHiddenGradients(cSpatialDScale.AsObject()))
      return false;

Merece atenção especial a STFSGrad, função wrapper do kernel homônimo. Ela reúne, dentro do módulo STFS, os erros provenientes de todas as subamostras e os propaga de volta aos dados originais.

   if(!STFSGrad(NeuronOCL))
      return false;
//---
   return true;
  }

Graças a esse mecanismo, o modelo aprende a identificar e distinguir padrões globais e locais do mercado, contribuindo para formar um modelo de previsão robusto e preciso.

Na prática, para o trader, isso significa que cada elemento do módulo, das subamostras aos blocos convolucionais, recebe feedback sobre sua relevância para a previsão final. Isso melhora a qualidade do treinamento e permite que o modelo lide de forma eficiente com dados de mercado ruidosos e variáveis.

O código completo da classe e de todos os seus métodos está disponível no anexo.



Arquitetura dos modelos

Depois de implementar todos os principais módulos do framework, passamos à etapa seguinte: descrever a arquitetura do modelo. Seguindo a abordagem dos autores da S3CE-Net, utilizamos como base o modelo do codificador de estado do ambiente do SEW-ResNeXt, que já demonstrou sua eficácia na extração de características relevantes de dados em fluxo contínuo.

Nessa arquitetura, incorporamos SSAM em três níveis, permitindo direcionar seletivamente a atenção para as características mais informativas e suas combinações. Essa abordagem faz com que a rede se concentre nos principais sinais do mercado, reduzindo a influência do ruído e das oscilações aleatórias, algo especialmente importante para dados financeiros em condições de alta volatilidade.

Para não sobrecarregar o texto do artigo e preservar sua legibilidade, não apresentaremos o esquema completo do codificador. Os detalhes da arquitetura estão disponíveis no anexo do artigo, onde cada camada e cada bloco são descritos em detalhes. O leitor poderá consultar por conta própria tanto os ajustes pontuais realizados na arquitetura quanto sua estrutura completa.

É importante destacar que o módulo STFS é utilizado exclusivamente na fase de treinamento. Em operação real, quando o modelo gera previsões em tempo real, as subamostras e as projeções separadas dos dados deixam de ser necessárias, e sua geração apenas aumenta o consumo de recursos. Para reduzir a carga computacional, separamos o STFS em um modelo independente, utilizado somente durante o treinamento.

O modelo começa com a criação da camada de dados de entrada, correspondente às características analisadas e à janela de observação.

//--- STFS
   stfs.Clear();
//--- Input layer
   if(!(descr = new CLayerDescription()))
      return false;
   descr.type = defNeuronBaseOCL;
   descr.count = (window * count);
   descr.activation = None;
   descr.optimization = ADAM;
   if(!stfs.Add(descr))
     {
      delete descr;
      return false;
     }

Na etapa seguinte, adicionamos uma camada STFS, na qual são definidos os parâmetros de canais e unidades de entrada e saída. Essa camada desempenha uma função essencial: forma cinco subamostras dos dados e calcula uma previsão para cada uma delas utilizando a função de ativação TANH especificada. É essa camada que viabiliza o treinamento paralelo em diferentes projeções do mercado, aumentando a robustez do modelo e sua capacidade de identificar sinais relevantes.

//--- layer 1
   if(!(descr = new CLayerDescription()))
      return false;
   descr.type = defNeuronSTFS;
     {
      uint temp[] = {window,              // Chanels In
                     BarDescr             // Chanels Out
                    };
      if(ArrayCopy(descr.windows, temp) < (int)temp.Size())
         return false;
     }
     {
      uint temp[] = {count,               // Units In
                     NForecast            // Units Out
                    };
      if(ArrayCopy(descr.units, temp) < (int)temp.Size())
         return false;
     }
   descr.activation = TANH;
   descr.optimization = ADAM;
   if(!stfs.Add(descr))
     {
      delete descr;
      return false;
     }

A configuração é concluída com uma camada BatchNorm, que normaliza a saída do bloco STFS, mantém a estabilidade do treinamento e evita deslocamentos excessivos dos gradientes. Neste caso, a camada desempenha ainda outra função importante: aprende a distribuição dos dados-alvo e a adiciona aos valores previstos gerados pelo módulo STFS.

//--- layer 2
   if(!(descr = new CLayerDescription()))
      return false;
   descr.type = defNeuronBatchNormOCL;
   descr.count = NForecast*BarDescr;
   descr.batch = BatchSize;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!stfs.Add(descr))
     {
      delete descr;
      return false;
     }

A separação do STFS em um modelo independente permite separar a fase de treinamento do uso em produção, reduzindo a carga computacional em tempo real sem perder as vantagens do treinamento paralelo em diferentes projeções do mercado.

Quanto à arquitetura dos modelos do Ator e do Crítico, ela foi integralmente reaproveitada do nosso trabalho anterior. Isso permite manter soluções já consolidadas para a geração de ações e a avaliação de sua qualidade, além de integrar novos componentes sem comprometer a estabilidade do treinamento. Como resultado, obtemos um modelo completo que combina abordagens tradicionais de gestão da estratégia no mercado com métodos modernos de extração de características relevantes e previsão robusta.



Testes

Concluímos a integração de todos os principais componentes do framework e chegou a hora de avaliar a eficácia das soluções implementadas. Antes disso, porém, é necessário treinar e testar cuidadosamente a estratégia com dados históricos e simulados, como se estivéssemos desenvolvendo as habilidades de um trader experiente e avaliando a capacidade do algoritmo de atuar nas mais diversas condições de mercado.

A primeira etapa, o treinamento offline, será realizada com dados históricos do par de moedas EURUSD, no timeframe H1, no período de janeiro de 2024 a 4junho de 2025. Esse período serviu como uma espécie de campo de treinamento. O modelo aprendeu a reconhecer padrões históricos, compreender a dinâmica dos preços e os volumes negociados, identificar relações entre as principais características e utilizá-las nas previsões. Graças ao módulo STFS, ele passou a explorar em paralelo a representação global e a representação local dos dados, formando cinco projeções diferentes, o que aumentou a precisão e a robustez das previsões. Pode-se dizer que o modelo desenvolveu a intuição de um trader aliada ao rigor matemático da análise de dados.

A etapa seguinte, um ajuste fino online no testador de estratégias do MetaTrader 5, permitiu que o modelo trabalhasse com fluxos de dados em tempo real. Nesse estágio, ele aprendeu a lidar com a dinâmica do mercado em tempo real, a manter a estabilidade em meio ao ruído de mercado, a ajustar suas ações em condições de baixa liquidez e a reagir instantaneamente a picos abruptos de preço. Os blocos SSAM direcionavam a atenção para as características mais informativas, mesmo quando os dados apresentavam instabilidade.

A verificação final foi realizada com dados do período de julho a setembro de 2025, totalmente novos e nunca utilizados anteriormente. Todos os parâmetros obtidos nas etapas anteriores foram carregados sem alterações, permitindo uma avaliação imparcial da capacidade de generalização do modelo. Os resultados confirmam que uma abordagem gradual de treinamento e adaptação, com o uso de STFS e SSAM, permite ao modelo analisar o mercado e gerar previsões sobre a evolução das principais características do mercado, nas quais o trader pode se apoiar ao tomar decisões de trading em condições reais.

Os resultados dos testes do modelo demonstram alta estabilidade e precisão nas previsões. O saldo da conta apresentou um crescimento consistente, passando de um depósito inicial de US$ 100 para US$ 108,75, enquanto o equity acompanhou suavemente o saldo, o que demonstra o funcionamento correto do mecanismo de distribuição das previsões e de gestão de riscos.

O drawdown máximo do saldo foi de apenas 2,07%, e o do equity, 4,51%, indicando baixo nível de risco e robustez da estratégia mesmo diante das oscilações do mercado. O fator de lucro geral foi de 2,60, confirmando a capacidade do modelo de selecionar sinais lucrativos com eficiência.

Quanto à estrutura das operações, das 16 operações de trading realizadas, 8 foram lucrativas, o que corresponde a uma taxa de acerto de 50%. O lucro médio por operação lucrativa foi de 1,78, enquanto a perda média ficou limitada a 0,69, demonstrando um bom equilíbrio entre rentabilidade e risco. O maior lucro em uma única operação chegou a 5,02, enquanto o maior prejuízo ficou limitado a 1,99, o que também confirma a postura cautelosa do modelo na gestão de capital.

O modelo que combina SSAM e STFS apresentou um crescimento de capital gradual e contido, com baixo drawdown e uma boa relação entre lucro e risco. O resultado é promissor, mas ainda limitado do ponto de vista estatístico. Antes de levar a estratégia para o trading real, será necessário realizar testes mais abrangentes e de estresse, confirmando a robustez de seu comportamento em períodos de mercado prolongados e adversos.



Conclusão

Percorremos todo o caminho, da ideia até uma ferramenta operacional, como se estivéssemos montando um mecanismo de relojoaria de precisão: com cuidado, passo a passo e atenção aos detalhes.

Com os recursos do MQL5, implementamos os principais blocos da S3CE-Net:

  • incorporamos o SSAM ao codificador SEW-ResNeXt,
  • adaptamos o STFS ao vetor de características do mercado,
  • preparamos um kernel OpenCL para a geração paralela de cinco representações.

A principal ideia de engenharia está na separação dos modos. O STFS funciona de maneira eficiente, mas deve ser usado na fase de treinamento. Em produção, mantemos apenas a representação global. Isso reduz o consumo de recursos e preserva a previsibilidade do comportamento. Há ainda um detalhe prático importante: a ausência de uma função de ativação dentro do módulo não é um capricho, mas um requisito para formar corretamente os tensores-alvo e propagar os gradientes entre as projeções.

Os testes realizados no EURUSD, timeframe H1, no período de julho a setembro de 2025, apresentaram resultados promissores. O modelo adota uma abordagem conservadora. Tudo isso constitui uma boa base, mas ainda não permite tirar conclusões definitivas sobre o desempenho da estratégia no longo prazo.

O trader deve ter em mente um ponto importante: as métricas atuais inspiram otimismo, mas a amostra estatística ainda é pequena. Métricas elevadas em uma amostra reduzida podem ser enganosas. Daqui em diante, nossa tarefa não é comemorar a vitória, mas testar a robustez desses resultados.


Referências


Programas utilizados neste artigo

#NomeTipoDescrição
1Study.mq5Expert AdvisorEA para treinamento offline de modelos
2StudyOnline.mq5Expert AdvisorEA para treinamento online de modelos
3Test.mq5Expert AdvisorEA para testar o modelo
4Trajectory.mqhBiblioteca da classeEstrutura que descreve o estado do sistema e a arquitetura dos modelos
5NeuroNet.mqhBiblioteca da classeBiblioteca de classes para criação de redes neurais
6NeuroNet.clBibliotecaBiblioteca de código do programa OpenCL

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

Arquivos anexados |
MQL5.zip (3218.97 KB)
Métodos de amostragem MCMC - Algoritmo de Metropolis-Hastings Métodos de amostragem MCMC - Algoritmo de Metropolis-Hastings
O algoritmo de Metropolis-Hastings é um método fundamental de Monte Carlo via cadeias de Markov (MCMC), amplamente utilizado para aproximar distribuições a posteriori na inferência bayesiana. O artigo apresenta os fundamentos teóricos do algoritmo, a implementação da classe MHSampler em MQL5 e exemplos de aplicação com análise das amostras obtidas.
Algoritmo de estrutura cristalina, Crystal Structure Algorithm (CryStAl) Algoritmo de estrutura cristalina, Crystal Structure Algorithm (CryStAl)
O artigo apresenta duas versões do Algoritmo de Estrutura Cristalina: a original e a modificada. O Crystal Structure Algorithm (CryStAl), publicado em 2021 e inspirado na física das estruturas cristalinas, foi apresentado como uma meta-heurística parameter-free para otimização global. No entanto, os testes revelaram um problema crítico no algoritmo. O artigo também apresenta a versão modificada CryStAlm, que corrige as principais deficiências da versão original.
Redes neurais no trading: Percepção adaptativa da dinâmica de mercado (STE-FlowNet) Redes neurais no trading: Percepção adaptativa da dinâmica de mercado (STE-FlowNet)
O framework STE-FlowNet oferece uma nova perspectiva para a análise de dados financeiros, reagindo a eventos reais do mercado, e não a timeframes fixos. Sua arquitetura preserva as dependências locais e temporais, permitindo rastrear até mesmo pequenos impulsos na dinâmica dos preços.
Análise da influência dos ciclos solares e lunares sobre os preços das moedas Análise da influência dos ciclos solares e lunares sobre os preços das moedas
E se os ciclos lunares e os padrões sazonais influenciarem os mercados cambiais? Este artigo mostra como traduzir conceitos astrológicos para a linguagem da matemática e do machine learning. Criei um sistema em Python com 88 atributos baseados em ciclos astronômicos, treinei o CatBoost com 15 anos de dados do EUR/USD e obtive resultados intrigantes. O código é aberto, os métodos podem ser verificados e as conclusões são inesperadas: a sabedoria antiga encontra o gradient boosting.