English Русский Deutsch 日本語
preview
Teoria dos grafos: Algoritmo de Dijkstra no trading

Teoria dos grafos: Algoritmo de Dijkstra no trading

MetaTrader 5Exemplos |
30 0
Hlomohang John Borotho
Hlomohang John Borotho


Introdução

Neste artigo, analisaremos a implementação do algoritmo de Dijkstra, um conceito fundamental da teoria dos grafos conhecido por sua eficiência na resolução de problemas de busca do caminho mais curto. Esse algoritmo, tradicionalmente aplicado em roteamento e otimização de redes, será adaptado aos mercados financeiros por meio da modelagem do movimento do preço como um grafo ponderado. Aqui, os nós representam níveis de preços ou intervalos de tempo, enquanto as arestas refletem o custo ou a probabilidade de transição entre eles.

Avaliaremos a aplicabilidade do método de Dijkstra para prever o próximo provável conjunto de dados de preços, que, na prática, define o "caminho mais curto" pelo qual o preço pode se deslocar de sua posição atual até um valor futuro. Ao tratar a dinâmica do mercado como um grafo, determinaremos a trajetória mais provável para otimizar decisões de trading com base na menor resistência ou no menor custo.

A teoria dos grafos fornece uma base poderosa para analisar estruturas complexas de mercado, e o algoritmo de Dijkstra oferece uma forma sistemática de percorrê-las. Ao interpretar os movimentos de preço como arestas com pesos, como a volatilidade, podemos calcular o caminho ideal que minimiza o risco ou maximiza a eficiência da trajetória.

O array de preço previsto atua, em essência, como a distância mais curta entre o preço atual e os níveis futuros, oferecendo aos traders um método de previsão de tendências orientado por dados. Essa abordagem une trading algorítmico e matemática computacional, demonstrando como algoritmos clássicos de grafos podem revelar oportunidades ocultas em séries temporais financeiras.


Abordagem fundamental de Dijkstra

Terminologia Nossa interpretação:
Grafo : conjunto de nós e arestas. Grafo: estrutura do gráfico formada por máximas e mínimas de swing. Cada ponto de swing passa a ser um , e cada caminho de preço entre eles se torna uma aresta.
Peso : custo de deslocamento de um nó para outro. Peso: custo ou esforço necessário para mover o preço entre dois pontos de swing. Pode ser a distância absoluta de preço.
Nó de origem: ponto inicial do algoritmo. Nó de origem: o ponto de swing confirmado mais recente mais recente, a última máxima ou mínima que o preço ainda não rompeu. Esse é nosso ponto de partida para calcular os caminhos mais curtos.
Conjunto visitado: nós cujo processamento já concluímos. Conjunto visitado: todos os pontos de swing que o algoritmo já avaliou e não revisitará. Em termos de trading, são pontos de swing já rompidos pelo preço ou em direção aos quais a negociação está sendo conduzida.
Tabela de distâncias : acompanha a menor distância até cada nó. Tabela de distâncias : mapeia cada nó para seu menor "custo para ser alcançado" a partir do nó de origem a partir do nó de origem. No trading, ela informa qual é o menor custo, ou a menor dificuldade, para o preço se deslocar do ponto atual para qualquer outro ponto de swing.

Passo a passo:

1. Inicialização:

  • Definimos a distância até o nó de origem como 0.
  • Definimos a distância até todos os outros nós como infinito.
  • Criamos uma fila de prioridades, ou min-heap (heap mínimo), para sempre selecionar o nó com a menor distância conhecida.

2. Visitamos o nó não visitado mais próximo:

  • Começamos pelo nó de origem.
  • Para cada vizinho, calculamos:
new_distance = distance_to_current + edge_weight

Se o valor de new_distance for menor que a distância conhecida anteriormente, atualizamos esse valor.

3. Marcamos o nó atual como visitado:

  • Depois de visitá-lo, não retornamos mais a ele.

4. Repetição:

  • Continuamos visitando o próximo nó não visitado mais próximo.
  • Repetimos até que todos os nós tenham sido visitados ou até que o caminho mais curto seja encontrado.
  • пока остаются непосещенные узлы:
        выберем узел с наименьшим предполагаемым расстоянием
        для каждого соседа:
            если новый путь к соседу короче:
                обновим кратчайшее расстояние
        отметим текущий узел как посещенный



Começamos

//+------------------------------------------------------------------+
//|                                               Dijkstars Algo.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 strict
#include <Trade/Trade.mqh>
CTrade trade;

Começamos incluindo o arquivo trade.mqh, que nos dá acesso às funções nativas de trading do MQL5 por meio da classe CTrade. Essa classe fornece métodos para abrir, modificar e fechar operações programaticamente. Após incluir o arquivo, criamos uma instância de CTrade chamada trade, que usaremos no EA para enviar comandos de trading, como Buy(), Sell() e PositionOpen(). Essa configuração é necessária para automatizar a execução de ordens no EA.

// Input Parameters
input int    TakeProfit   = 1000;     
input int   StopLoss = 385;
input double  In_Lot = 0.01;
input int    LeftBars    = 3;
input int    RightBars   = 3;
input int    MaxSwings   = 50;
input double Lots        = 0.1;
input double PointBuffer = 10;
input int    Slippage    = 5;
ENUM_TIMEFRAMES TimeFrame;

Nesta seção, definimos os parâmetros de entrada do EA, permitindo que o trader ajuste parâmetros principais diretamente pela interface do EA. Os parâmetros "TakeProfit" e "StopLoss" definem o alvo e o risco em pontos, enquanto "In_Lot" e "Lots" determinam o tamanho da operação. Os parâmetros "LeftBars" e "RighBars" são usados para identificar máximas e mínimas de swing comparando as barras com suas vizinhas. "MaxSwings" limita a quantidade de pontos de swing monitorados, e "pointBuffer" adiciona uma distância extra ao SL/TP como margem de proteção. "Slippage" define o desvio máximo de preço permitido na execução da ordem, enquanto "TimeFrame" especifica o período gráfico que será analisado pelo EA.

// Node Structure
struct SwingPoint {
   int index;
   datetime time;
   double price;
   bool isHigh;
   bool visited;
   double distance;
   bool used;
   int previous;
};

Essa estrutura define o modelo de SwingPoint, que representa um nó no sistema de trading baseado no algoritmo de Dijkstra. Cada ponto de swing contém informações importantes:

  • Index é o número da barra em que o swing foi detectado.
  • Time é o marca temporal exata dessa barra.
  • Price é o valor máximo ou mínimo desse swing.
  • IsHigh indica se esse é uma máxima de swing (true) ou uma mínima de swing (false).
  • Visited ajuda a acompanhar quais nós já foram processados pelo algoritmo.
  • Distance armazena o custo calculado a partir do nó de origem pela busca de caminho de Dijkstra.
  • Used sinaliza que esse swing já foi usado na tomada de decisões de trading.
  • Previous acompanha o nó previous na cadeia do caminho mais curto.
SwingPoint swingPoints[];

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit() {
   Print("Dijkstra Swing EA initialized");
   return INIT_SUCCEEDED;
}

Aqui é declarado o array dinâmico swingPoints[] para armazenar todas as máximas e mínimas de swing detectadas, na forma de estruturas SwingPoint, no gráfico. Esse array será preenchido e usado em todo o EA para representar os nós no grafo de price action. Na função OnInit(), o EA apenas imprime no terminal uma mensagem confirmando que o "Dijkstra Swing EA" foi inicializado com sucesso e retorna INIT_SUCCEEDED, sinalizando uma inicialização adequada.

//+------------------------------------------------------------------+
//| Detect swing highs and lows                                      |
//+------------------------------------------------------------------+
void DetectSwings(int left, int right) {
   ArrayResize(swingPoints, 0);

   int totalBars = Bars(_Symbol, PERIOD_CURRENT) - right;
   for (int i = left; i < totalBars; i++) {
      bool isHigh = true, isLow = true;
      double high = High(i), low = Low(i);

      for (int j = 1; j <= left; j++) {
         if (High(i - j) >= high) isHigh = false;
         if (Low(i - j) <= low) isLow = false;
      }
      for (int j = 1; j <= right; j++) {
         if (High(i + j) >= high) isHigh = false;
         if (Low(i + j) <= low) isLow = false;
      }

      if (isHigh || isLow) {
         int idx = ArraySize(swingPoints);
         ArrayResize(swingPoints, idx + 1);
         swingPoints[idx].index = i;
         swingPoints[idx].time = Time(i);
         swingPoints[idx].price = isHigh ? high : low;
         swingPoints[idx].isHigh = isHigh;
         swingPoints[idx].visited = false;
         swingPoints[idx].distance = DBL_MAX;
         swingPoints[idx].previous = -1;

         if (idx >= MaxSwings) break;
      }
   }
}

Essa função DetectSwings() identifica máximas e mínimas de swing no gráfico de preços comparando cada candle com as barras vizinhas. Ela começa limpando o array existente SwingPoints com ArrayResize, permitindo uma nova detecção a cada chamada. A função percorre cada barra do gráfico, começando no índice left até totalBars - right, e verifica se a barra atual (i) corresponde a uma máxima ou mínima de swing.

Para determinar se uma barra é uma máxima de swing, verifica-se se seu preço máximo supera as máximas das barras anteriores "à esquerda" e das barras seguintes "à direita". De modo semelhante, uma mínima de swing é confirmada se seu preço mínimo for menor que as mínimas vizinhas anteriores e seguintes. Se pelo menos uma das condições for atendida, a barra é considerada um ponto de swing confirmado mais recente. Essa comparação local garante que apenas picos e quedas relevantes de preço sejam registrados como nós de swing.

Ao detectar um swing, ele é armazenado no array SwingPoints[] com todos os dados correspondentes: índice, horário, preço, indicação de ser máxima ou mínima, além dos valores padrão para a busca de caminho, como visited, distance e previous. Essa estrutura dá suporte à análise posterior com o algoritmo de Dijkstra para avaliar probabilidades de caminhos entre swings. O loop é encerrado antecipadamente se a quantidade de swings detectados atingir o valor de MaxSwings, evitando uso excessivo de memória ou problemas de desempenho.

//+------------------------------------------------------------------+
//| Apply Dijkstra's algorithm                                       |
//+------------------------------------------------------------------+
void ApplyDijkstra() {
   if (ArraySize(swingPoints) == 0) return;

   swingPoints[0].distance = 0;

   for (int i = 0; i < ArraySize(swingPoints); i++) {
      int u = -1;
      double minDist = DBL_MAX;

      for (int j = 0; j < ArraySize(swingPoints); j++) {
         if (!swingPoints[j].visited && swingPoints[j].distance < minDist) {
            minDist = swingPoints[j].distance;
            u = j;
         }
      }

      if (u == -1) break;

      swingPoints[u].visited = true;

      for (int v = 0; v < ArraySize(swingPoints); v++) {
         if (!swingPoints[v].visited) {
            double cost = MathAbs(swingPoints[u].price - swingPoints[v].price);
            if (swingPoints[u].distance + cost < swingPoints[v].distance) {
               swingPoints[v].distance = swingPoints[u].distance + cost;
               swingPoints[v].previous = u;
            }
         }
      }
   }
}

Nesta função, implementamos o algoritmo de Dijkstra para calcular o caminho mais curto do primeiro ponto de swing até todos os demais pontos de swing, tratando o movimento do preço entre máximas e mínimas de swing como um grafo ponderado. Primeiro, verificamos se existem pontos de swing. Se o array estiver vazio, a função retorna imediatamente. Em seguida, ela define a "distância" do primeiro nó, o pivô inicial, como "0", marcando o nó de origem na busca de caminho.

 O algoritmo passa a executar um loop no qual, a cada iteração, seleciona o ponto de swing não visitado com a menor distance conhecida. Esse nó (u) é marcado como visitado, e o algoritmo avalia todos os seus vizinhos não visitados. Para cada um desses vizinhos (v), é calculado o custo, ou peso, da transição do nó u para v com base na diferença absoluta de preço entre eles. Se o custo acumulado para chegar a v por meio de u for menor que a distância registrada atualmente, a função atualiza a distance de v e registra u como seu nó previous.

Esse procedimento continua até que todos os pontos de swing disponíveis tenham sido visitados ou até que não restem nós não visitados disponíveis. Ao final da função, cada ponto de swing contém o menor custo acumulado a partir do nó de origem e um ponteiro para o nó previous ao longo desse caminho ideal. Essas informações permitem que o EA acompanhe o caminho mais eficiente ao longo da estrutura recente do mercado e determine quais pontos de swing o preço tem maior probabilidade de visitar em seguida, formando a base para a geração de sinais de trading inteligentes.

//+------------------------------------------------------------------+
//| Visualize Swing Points and Connections                           |
//+------------------------------------------------------------------+
void VisualizeSwings() {
   for (int i = 0; i < ArraySize(swingPoints); i++) {
      string objName = "Swing_" + IntegerToString(i);
      ObjectDelete(0, objName);

      ObjectCreate(0, objName, OBJ_ARROW, 0, swingPoints[i].time, swingPoints[i].price);
      ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, swingPoints[i].isHigh ? 233 : 234);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, swingPoints[i].isHigh ? clrRed : clrBlue);
   }

   for (int i = 1; i < ArraySize(swingPoints); i++) {
      int prev = swingPoints[i].previous;
      if (prev != -1) {
         string lineName = "Line_" + IntegerToString(i);
         ObjectDelete(0, lineName);

         ObjectCreate(0, lineName, OBJ_TREND, 0,
                      swingPoints[prev].time, swingPoints[prev].price,
                      swingPoints[i].time, swingPoints[i].price);
         ObjectSetInteger(0, lineName, OBJPROP_COLOR, clrGray);
         ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 1);
      }
   }
}

A função VisualizeSwings() é responsável por exibir os pontos de swing detectados e as conexões entre eles diretamente no grafo, ajudando os traders a confirmar visualmente a estrutura e a lógica usadas pelo EA. No primeiro loop for, ela percorre o array swingPoints[] e cria objetos do tipo seta para cada swing. Antes de criar um novo objeto, ela remove todos os objetos existentes com o mesmo nome para evitar poluição visual. Cada seta recebe um símbolo específico: vermelho para máximas de swing ("código de seta 233") e azul para mínimas de swing ("código de seta 234"), permitindo diferenciá-las visualmente.

No segundo loop for, a função desenha linhas entre cada ponto de swing e seu respectivo nó anterior, definido pelo algoritmo de Dijkstra. Essas linhas representam as conexões de caminho mais curto usadas para avaliar possíveis caminhos de trading. Novamente, antes de desenhar um novo objeto, qualquer objeto de linha existente com o mesmo nome é removido. As linhas são criadas com OBJ_TREND e desenhadas em cinza, com largura padrão, preservando uma estrutura visual limpa e clara.

Essa visualização ajuda a validar a lógica de decisão do EA, permitindo observar quais pontos de swing foram identificados, como estão conectados e qual caminho foi escolhido com base no algoritmo de Dijkstra. Isso é especialmente útil durante testes em dados históricos ou em trading real, quando é necessário confirmar que o EA analisa a estrutura do mercado conforme previsto.

double High(int index){return (iHigh(_Symbol, _Period, index));}
double Low(int index){return (iLow(_Symbol, _Period, index));}
datetime Time(int index){return (iTime(_Symbol, _Period, index));}

Essas três funções auxiliares, High(), Low() e Time(), são funções encapsuladoras simples em torno das funções nativas do MQL5: iHigh(), iLow() e iTime(). Elas fornecem acesso fácil ao preço máximo, ao preço mínimo e ao horário de abertura de uma barra específica, com base no índice fornecido, no símbolo atual e no timeframe atual. Com o uso dessas funções abreviadas, o código fica mais limpo e legível, especialmente ao acessar repetidamente dados de barras durante a detecção ou visualização de swings.

//+------------------------------------------------------------------+
//|                          Filter and mark                         |
//+------------------------------------------------------------------+
void FilterAndMarkValidSwings(SwingPoint &points[]) {
   int count = ArraySize(points);
   if(count < 2) return;

   for(int i = 0; i < count; i++) {
      if(points[i].used) continue;

      bool isValid = true;
      double swingPrice = points[i].price;
      int swingIndex = points[i].index;

      // Scan forward in time from the swing point
      for(int j = swingIndex - 1; j >= 0; j--) {
         double high = iHigh(_Symbol, TimeFrame, j);
         double low  = iLow(_Symbol, TimeFrame, j);

         // Invalidate swing high if price went higher later
         if(points[i].isHigh && high > swingPrice) {
            isValid = false;
            break;
         }

         // Invalidate swing low if price went lower later
         if(!points[i].isHigh && low < swingPrice) {
            isValid = false;
            break;
         }
      }

      if(isValid) {
         points[i].used = true;

         // Draw object on chart
         string objName = points[i].isHigh ? 
            StringFormat("SwingHigh_%d", TimeToString(iTime(_Symbol, TimeFrame, swingIndex))) :
            StringFormat("SwingLow_%d", TimeToString(iTime(_Symbol, TimeFrame, swingIndex)));

         color swingColor = points[i].isHigh ? clrRed : clrBlue;

         ObjectCreate(0, objName, OBJ_HLINE, 0, 0, swingPrice);
         ObjectSetInteger(0, objName, OBJPROP_COLOR, swingColor);
         ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);
         ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
      }
   }
}

A função FilterAndMarkValidSwings() refina a lista de pontos de swing, determinando quais deles ainda são válidos e não foram invalidados por movimentos futuros do preço. Ela recebe o array de referências SwingPoints e o percorre, ignorando aqueles que já foram marcados como used. Para cada swing potencial, ela assume que o ponto é válido e, em seguida, verifica a validade com base no movimento histórico do preço para confirmar se o preço rompeu o limite desse swing após sua formação.

Para determinar a validade, a função varre as barras anteriores em ordem reversa a partir do índice do swing. Para uma máxima de swing, ela verifica se alguma vela futura teve uma máxima superior a ela; para uma mínima de swing, verifica se alguma vela teve uma mínima mais baixa. Se essa condição for detectada, o ponto de swing é considerado inválido, pois o preço efetivamente rompeu esse swing. Portanto, ele não é marcado nem usado nos cálculos posteriores. Se essa condição não for identificada, o ponto de swing é considerado válido e marcado como used.

Para cada swing válido, a função desenha uma linha horizontal no gráfico para marcá-lo visualmente. A linha é exibida como pontilhada e colorida em vermelho para máximas de swing ou em azul para mínimas de swing. O objeto recebe um nome com base no tipo de swing e no horário da barra em que ele foi detectado. Esse feedback visual ajuda os traders a identificar instantaneamente quais pontos de swing o EA considera fortes e ainda não tocados pelo preço, aumentando a confiança e facilitando a depuração da lógica durante a análise ou o trading.

//+------------------------------------------------------------------+
//|                        Cleaning up old swings                    |
//+------------------------------------------------------------------+
void CleanOldSwingObjects(int keepBars = 100) {
   datetime oldestDate = iTime(_Symbol, TimeFrame, keepBars);
   int total = ObjectsTotal(0);

   for(int i = total - 1; i >= 0; i--) {
      string name = ObjectName(0, i);
      if(StringFind(name, "SwingHigh_") == 0 || StringFind(name, "SwingLow_") == 0) {
         datetime swingTime = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME);
         if(swingTime < oldestDate) {
            ObjectDelete(0, name);
         }
      }
   }
}

Aqui, a função é responsável por remover do gráfico elementos visuais desatualizados relacionados a swings, preservando a clareza e a eficiência. Ela define um valor-limite para objetos "antigos" usando o parâmetro keepBars, que extrai a marca temporal da barra localizada keepBars candles antes. Todos os objetos de swing criados antes dessa marca temporal são considerados desatualizados. Em seguida, a função percorre todos os objetos gráficos no gráfico em ordem reversa e verifica se seus nomes começam com "SwingHigh_" ou "SwingLow_", o que os identifica como marcadores de swing.

Para cada um desses objetos de swing, ela extrai o horário de criação e o compara com a marca temporal de corte (oldestDate). Se o horário do objeto for mais antigo, ele é removido do gráfico com objectDelete(). Esse procedimento garante que o gráfico permaneça limpo, exibindo apenas pontos de swing recentes e relevantes. Ele também ajuda a evitar degradação de desempenho ao longo do tempo, especialmente quando o EA roda em históricos mais longos ou em mercados reais, onde muitos swings se acumulam.

//+------------------------------------------------------------------+
//| Generate Signal & Trade                                          |
//+------------------------------------------------------------------+
void GenerateSignalAndTrade() {
   if (ArraySize(swingPoints) < 2) return;

   int last = ArraySize(swingPoints) - 1;
   int prev = swingPoints[last].previous;
   if (prev == -1) return;

   double entry = swingPoints[last].price;
   double reference = swingPoints[prev].price;
   double sl, tp;
   bool isBuy = entry > reference, isSell = entry < reference;

   SetSLTP(entry, reference, isBuy, sl, tp);

   if (PositionSelect(_Symbol)) return;

   if (isBuy)
      ExecuteTrade(ORDER_TYPE_BUY);
   else if(isSell)
      ExecuteTrade(ORDER_TYPE_SELL); 
}

A função GenerateSignalAndTrade() é responsável por gerar um sinal de trading com base na direção do último caminho encontrado pelo algoritmo de Dijkstra pelos pontos de swing. Primeiro, ela verifica se existem pelo menos dois pontos de swing para comparação e que o último ponto de swing tenha um nó previous válido. Em seguida, extrai o preço do último swing e do swing anterior associado a ele, usando a relação entre os preços para determinar a direção da operação: se o último preço for maior que o anterior, é um sinal de compra; se for menor, é um sinal de venda.

Após determinar a direção, a função calcula os níveis de Stop Loss e Take Profit com a função SetSLTP(), que os baseia na distância entre os pontos de swing. Antes de abrir a operação, verifica se já existe uma posição aberta no símbolo para evitar duplicidades. Por fim, executa a operação com a função ExecuteTrade(), passando o tipo de ordem correspondente. Essa lógica garante que as operações sejam executadas apenas quando um caminho direcional claro e estruturalmente justificado for identificado entre pontos de swing válidos.

//+------------------------------------------------------------------+
//| Calculate SL and TP based on distance to previous node           |
//+------------------------------------------------------------------+
void SetSLTP(double entry, double ref, bool isBuy, double &sl, double &tp) {
   double distance = MathAbs(entry - ref) + PointBuffer * _Point;
   if (isBuy) {
      sl = entry - distance;
      tp = entry + distance;
   } else {
      sl = entry + distance;
      tp = entry - distance;
   }
}

A função SetSLTP() calcula os níveis de Stop Loss (sl) e Take Profit (tp) da operação com base na distância entre o preço de entrada atual e o preço de referência, normalmente o ponto de swing anterior. Primeiro, ela calcula a diferença absoluta de preço entre esses dois pontos e adiciona um pequeno buffer, em pontos, como margem de proteção. Se a operação for de compra, o Stop Loss é posicionado abaixo da entrada, e o Take Profit, acima dela; para uma venda, o Stop Loss é posicionado acima da entrada, e o Take Profit, abaixo dela. Isso mantém uma distribuição simétrica de risco e retorno em relação à estrutura do swing, ajudando o EA a acompanhar o movimento do preço com níveis de SL e TP significativos e baseados na estrutura.

//+------------------------------------------------------------------+
//| Execute trade with risk parameters                               |
//+------------------------------------------------------------------+
void ExecuteTrade(ENUM_ORDER_TYPE tradeType){

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double price = (tradeType == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
                                                  SymbolInfoDouble(_Symbol, SYMBOL_BID);

   // Convert StopLoss and TakeProfit from pips to actual price distances
   double sl_distance = StopLoss * point;
   double tp_distance = TakeProfit * point;
   
   double sl = (tradeType == ORDER_TYPE_BUY) ? price - sl_distance :
                                               price + sl_distance;
   
   double tp = (tradeType == ORDER_TYPE_BUY) ? price + tp_distance :
                                               price - tp_distance;

   trade.PositionOpen(_Symbol, tradeType, In_Lot, price, sl, tp, NULL);
}

A função ExecuteTrade() é responsável por enviar a ordem de trading com parâmetros de risco predefinidos. Ela começa determinando o preço atual de mercado: para uma ordem de compra, usa-se o preço Ask; para uma ordem de venda, usa-se o preço Bid. Em seguida, calcula os níveis de Stop Loss e Take Profit convertendo os valores de entrada (stopLoss e TakeProfit) de pontos para distâncias efetivas em preço com base no valor de ponto do símbolo. Dependendo de a operação ser de compra ou venda, ela posiciona o Stop Loss e o Take Profit acima ou abaixo do preço de entrada, conforme o caso.

Por fim, ela usa o método PositionOpen() da classe CTrade para executar a operação com os parâmetros calculados, incluindo tamanho do lote, direção, preço de entrada, SL, TP e ausência de comentários personalizados. Isso garante que as operações sigam um modelo de risco uniforme, independentemente da direção do mercado.

//+------------------------------------------------------------------+
//| OnTick                                                           |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, _Period, 0);

   if (currentBarTime != lastBarTime) {
      lastBarTime = currentBarTime;

      DetectSwings(LeftBars, RightBars);
      ApplyDijkstra();
      VisualizeSwings();
      GenerateSignalAndTrade();
      FilterAndMarkValidSwings(swingPoints);
      CleanOldSwingObjects();

   }
}

Por fim, a função OnTick() é o ciclo principal de execução do EA, acionado a cada novo tick. Para evitar processamento desnecessário, usa-se a variável estática lastBarTime para determinar se uma nova barra foi formada, comparando-a com o horário de abertura da barra atual. Quando uma nova barra é detectada, a função atualiza lastBarTime e executa a lógica principal: detecção de novas máximas e mínimas de swing (DetectSwings), aplicação do algoritmo de Dijkstra para buscar o caminho mais eficiente entre swings (ApplyDijkstra), visualização dos swings e de suas conexões no gráfico (VisualiseSwings), geração e execução de sinais de trading com base na direção do caminho (GenerateSignalAndTrade), filtragem dos pontos de swing inválidos (FilterAndMarkValidSwings) e, por fim, limpeza dos objetos de swing antigos para manter o gráfico organizado (CleanOldSwingObjects).

Essa estrutura permite que o EA processe a estrutura de mercado de forma inteligente.



Resultados do backtest

O backtest foi avaliado no timeframe 1H durante uma janela de teste de 2 meses, de 1º de maio de 2025 a 20 de junho de 2025, com as seguintes configurações de entrada:

  • TP em pontos = 1000
  • Stop Loss = 385
  • lotes de entrada = 0,01
  • barras à esquerda = 3
  • barras à direita = 3
  • máx. de swings = 50
  • buffer de pontos = 10,0
  • slippage = 5

Conclusão

Em resumo, criamos um EA totalmente funcional em MQL5 que interpreta a estrutura do mercado financeiro por meio do algoritmo de Dijkstra, aplicado às máximas e mínimas de swing como nós de um grafo. O sistema detecta pontos de swing relevantes a cada nova barra, filtra os inválidos que o preço já rompeu e trata os swings válidos como vértices no algoritmo de busca de caminho. Em seguida, usa a distância de preço como peso das arestas para calcular o caminho mais eficiente ao longo da estrutura do mercado, determinando a direção mais provável do movimento do preço.

Com base nessa análise, o EA gera sinais direcionais de trading e executa operações com níveis de Stop Loss e Take Profit devidamente calculados a partir da distância entre os pontos de swing. Ferramentas visuais, como setas e linhas de tendência, são desenhadas para exibir tanto os pontos de swing detectados quanto os caminhos calculados, enquanto Sem alteração obrigatória.

Por fim, vale observar que este EA vai além do trading tradicional baseado em indicadores, integrando um algoritmo de teoria dos grafos à análise do movimento do preço, o que permite tomar decisões de trading mais estruturadas e lógicas. Ao alinhar as entradas de trading com a geometria dos swings de mercado e fazendo com que cada nó seja usado apenas uma vez enquanto permanecer válido enquanto permanecer válido, o sistema imita o fluxo natural do preço pelos níveis de suporte e resistência. A construção modular, com funções de detecção, validação, busca de caminho, execução e visualização, também permite aprimorar, expandir ou realizar backtests adicionais da estratégia com facilidade com facilidade. Este projeto estabelece a base para um sistema de trading inteligente e adaptativo, que trata o movimento do preço como uma rede navegável" ou "rede de rotas, unindo a teoria de estruturas de dados ao comportamento do mercado.

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

Arquivos anexados |
Dijkstras_Algo.mq5 (21.01 KB)
Análise espectral singular em MQL5 Análise espectral singular em MQL5
Este artigo serve como guia para quem ainda não conhece o conceito de análise espectral singular e deseja adquirir conhecimento suficiente para aplicar as ferramentas integradas disponíveis em MQL5.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 57): Aprendizado Supervisionado com Média Móvel e Oscilador Estocástico Técnicas do MQL5 Wizard que você deve conhecer (Parte 57): Aprendizado Supervisionado com Média Móvel e Oscilador Estocástico
A Média Móvel e o Oscilador Estocástico são indicadores muito comuns que alguns traders podem não usar com frequência devido à sua natureza atrasada. Em uma “minissérie” de 3 partes que considera as 3 principais formas de aprendizado de máquina, buscamos verificar se esse viés contra esses indicadores é justificado ou se eles podem estar mantendo alguma vantagem. Realizamos nossa análise em Expert Advisors montados pelo wizard.
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.
Técnicas de reamostragem para avaliação de previsão e classificação em MQL5 Técnicas de reamostragem para avaliação de previsão e classificação em MQL5
Neste artigo, exploraremos e implementaremos métodos para avaliar a qualidade de modelos que utilizam um único conjunto de dados tanto para treinamento quanto para validação.