Discussão do artigo "Algoritmos avançados de execução de ordens em MQL5: TWAP, VWAP e ordens Iceberg"

 

Novo artigo Algoritmos avançados de execução de ordens em MQL5: TWAP, VWAP e ordens Iceberg foi publicado:

Um framework MQL5 que oferece a traders de varejo algoritmos de execução de nível institucional (TWAP, VWAP, Iceberg) por meio de um gerenciador de execução unificado e de um analisador de desempenho, para fracionar ordens e analisar o desempenho com mais suavidade e precisão.

"Claro", você talvez diga, dando de ombros, "mas eu não movimento volumes institucionais". A questão é justamente esta: você não precisa. Quer você opere meio lote ou alguns minilotes, a volatilidade ainda pode distorcer sua execução. Essas ferramentas ajudam você a:

  • Domar o slippage: Até ordens modestas podem sofrer desvios em mercados instáveis.
  • Aprimorar sua vantagem: Execuções escalonadas muitas vezes oferecem um preço médio melhor do que uma operação feita de uma só vez.
  • Manter o estado zen: Rotinas automatizadas eliminam a tentação de comprar ou vender em pânico.
  • Escalar sem perder precisão: À medida que sua conta cresce, a execução continua ajustada, independentemente do tamanho que suas ordens atinjam.
  • Permanecer discreto: Em especial, as ordens Iceberg ocultam o tamanho real da sua ordem, deixando algoritmos curiosos apenas tentando adivinhar.

No ambiente democratizado atual, as mesmas tecnologias de execução que antes exigiam orçamentos milionários agora podem rodar no seu terminal pessoal de trading. Ao adicionar à sua plataforma um código MQL5 refinado para estratégias TWAP, VWAP e Iceberg, você passa a contar com poder de fogo institucional sem sair do segmento de varejo.


Autor: N Soumik

 

Ótimo artigo!

Quais pares você recomenda para esse Algo?

Quais os períodos de tempo? M5, M30 etc.

Em qual sessão?

Obrigado e cordiais saudações

 
Ótimo artigo
Testando seu algoritmo.
No arquivo ExecutionAlgorithm.mqh, adicionei esta linha request.type_filling = ORDER_FILLING_IOC; ao colocar a ordem para corrigir o problema de colocação da ordem.
Testado novamente no M5, ele abriu apenas uma negociação por um período de 2 meses, nenhuma ordem parcial foi aberta.
Testado no H1, ele nunca aplicou o SL ou TP e todas as negociações foram fechadas com prejuízo.

Também durante a compilação, ele gera avisos
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 271 41
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 272 22
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 449 17
possível perda de dados devido à conversão de tipo de 'long' para 'double' PerformanceAnalyzer.mqh 222 17
possível perda de dados devido à conversão de tipo de 'long' para 'double' ExecutionManager.mqh 418 17


Sugira como testar o algoritmo,
período de tempo e quaisquer outras recomendações.
 
para corrigir os avisos
também durante a compilação, ele gera avisos
Possível perda de dados devido à conversão de tipo de "long" para "double" VWAP .mqh 271 41
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP .mqh 272 22
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP .mqh 449 17
possível perda de dados devido à conversão de tipo de 'long' para 'double' PerformanceAnalyzer .mqh 222 17
Possível perda de dados devido à conversão de tipo de 'long' para 'double' ExecutionManager .mqh 418 17


Alterei a linha de código
m_volumeProfile[intervalIndex] += rates[i].tick_volu

para
m_volumeProfile[intervalIndex] += (double)rates[i].tick_volume;

Isso corrigiu os avisos
Agora preciso de sua orientação em relação às minhas outras dúvidas, como
Time frame
E também
por que todas as negociações durante o backtest resultam em perda
como testar esse excelente trabalho de vocês...
[Excluído]  
i_vergo possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 271 41
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 272 22
possível perda de dados devido à conversão de tipo de 'long' para 'double' VWAP.mqh 449 17
possível perda de dados devido à conversão de tipo de 'long' para 'double' PerformanceAnalyzer.mqh 222 17
possível perda de dados devido à conversão de tipo de 'long' para 'double' ExecutionManager.mqh 418 17


Sugira como testar o algoritmo,
período de tempo e quaisquer outras recomendações.

Os avisos não são o problema, mas podem ser corrigidos rapidamente. Mas, sim, seria ótimo se o autor pudesse mostrar passo a passo quais configurações e entradas ele usou para o backtest.

 

Concordo com Dominic, pois os avisos são apenas avisos. Os resultados de I_Virgo provavelmente se devem ao fato de ele ter usado o período de tempo e o par de moedas errados. Pelo relatório do Back Test, de quase 2.000 barras, deve ter sido M1 ou M5 como o período de tempo com um par desconhecido.

Seria bom se a MQ adicionasse o período de tempo e o par ou pares de moedas e também separasse os resultados dos pares em mais detalhes no relatório de back-test para que pudéssemos replicar mais de perto os resultados do back-test do autor, bem como determinar sua aplicabilidade nos pares de moedas estrangeiras.


Também acho que é um ótimo artigo e planejo estudá-lo minuciosamente na expectativa de adaptar suas técnicas a outros EAs


CapeCoddah

 
//+------------------------------------------------------------------+
//| Classe base para todos os algoritmos de execução|
//+------------------------------------------------------------------+
class CExecutionAlgorithm
{
protected:
   string            m_symbol;           // Símbolo de negociação
   double            m_totalVolume;      // Volume total a ser executado
   double            m_executedVolume;   // Volume já executado
   double            m_remainingVolume;  // Volume restante para execução
   datetime          m_startTime;        // Hora de início da execução
   datetime          m_endTime;          // Hora de término da execução
   int               m_slippage;         // Deslizamento permitido em pontos
   bool              m_isActive;         // O algoritmo está ativo no momento
   
   // Estatísticas
   double            m_avgExecutionPrice; // Preço médio de execução
   int               m_totalOrders;       // Número total de pedidos feitos
   int               m_filledOrders;      // Número de pedidos atendidos
   
public:
   // Construtor
   CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
   
   // Destruidor
   virtual ~CExecutionAlgorithm();
   
   // Métodos virtuais a serem implementados por classes derivadas
   virtual bool      Initialize();
   virtual bool      Execute() = 0;
   virtual bool      Update() = 0;
   virtual bool      Terminate() = 0;
   
   // Métodos comuns
   bool              IsActive() { return m_isActive; }
   double            GetExecutedVolume() { return m_executedVolume; }
   double            GetRemainingVolume() { return m_remainingVolume; }
   double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
   
   // Métodos auxiliares
   bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0);
   bool              ModifyOrder(ulong ticket, double price, double sl, double tp);
   bool              CancelOrder(ulong ticket);
   void              UpdateAverageExecutionPrice(double price, double volume);
   
   // Método auxiliar para obter o modo de preenchimento apropriado
   ENUM_ORDER_TYPE_FILLING GetFillingMode();
};

//+------------------------------------------------------------------+
//| Construtor|
//+------------------------------------------------------------------+
CExecutionAlgorithm::CExecutionAlgorithm(string symbol, double volume, 
                                       datetime startTime, datetime endTime, 
                                       int slippage)
{
   m_symbol = symbol;
   m_totalVolume = volume;
   m_executedVolume = 0.0;
   m_remainingVolume = volume;
   m_startTime = startTime;
   m_endTime = endTime;
   m_slippage = slippage;
   m_isActive = false;
   
   m_avgExecutionPrice = 0.0;
   m_totalOrders = 0;
   m_filledOrders = 0;
}

//+------------------------------------------------------------------+
//| Destruidor|
//+------------------------------------------------------------------+
CExecutionAlgorithm::~CExecutionAlgorithm()
{
   // Limpar os recursos, se necessário
}

//+------------------------------------------------------------------+
//| Inicializar o algoritmo|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::Initialize()
{
   // Validar entradas
   if(m_symbol == "" || m_totalVolume <= 0.0)
   {
      Print("Invalid inputs for execution algorithm");
      return false;
   }
   
   // Verificar se o símbolo existe
   if(!SymbolSelect(m_symbol, true))
   {
      Print("Symbol not found: ", m_symbol);
      return false;
   }
   
   // Redefinir estatísticas
   m_executedVolume = 0.0;
   m_remainingVolume = m_totalVolume;
   m_avgExecutionPrice = 0.0;
   m_totalOrders = 0;
   m_filledOrders = 0;
   
   return true;
}

//+------------------------------------------------------------------+
//| Obter o modo de preenchimento apropriado para o símbolo
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
{
   // Obter modos de preenchimento de símbolos
   int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
   
   // Verifique os modos de preenchimento disponíveis em ordem de preferência
   if((filling_modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
      return ORDER_FILLING_FOK;
   else if((filling_modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
      return ORDER_FILLING_IOC;
   else
      return ORDER_FILLING_RETURN;
}

//+------------------------------------------------------------------+
//| Fazer um pedido|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
{
   // Validar entradas
   if(volume <= 0.0)
   {
      Print("Invalid order volume");
      return false;
   }
   
   // Preparar a solicitação
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.symbol = m_symbol;
   request.volume = volume;
   request.type = orderType;
   request.deviation = m_slippage;
   request.magic = 123456; // Número mágico para identificação
   
   // Definir ação e preço apropriados com base no tipo de ordem
   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
   {
      // Ordem de mercado
      request.action = TRADE_ACTION_DEAL;
      request.type_filling = GetFillingMode();
      
      if(orderType == ORDER_TYPE_BUY)
         request.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
      else
         request.price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
   }
   else
   {
      // Ordem pendente
      request.action = TRADE_ACTION_PENDING;
      if(price <= 0.0)
      {
         Print("Price must be specified for pending orders");
         return false;
      }
      request.price = price;
   }
   
   // Enviar o pedido
   if(!OrderSend(request, result))
   {
      Print("OrderSend error: ", GetLastError());
      return false;
   }
   
   // Verificar o resultado
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   // Atualizar estatísticas
   m_totalOrders++;
   
   // Para ordens a mercado, atualizar as estatísticas de execução imediatamente
   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
   {
      m_filledOrders++;
      UpdateAverageExecutionPrice(request.price, volume);
      m_executedVolume += volume;
      m_remainingVolume -= volume;
   }
   
   Print("Order placed successfully. Ticket: ", result.order, " Volume: ", volume, " Price: ", request.price);
   
   return true;
}

//+------------------------------------------------------------------+
//| Modificar um pedido existente|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
{
   // Preparar a solicitação
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_MODIFY;
   request.order = ticket;
   request.price = price;
   request.sl = sl;
   request.tp = tp;
   
   // Enviar a solicitação de modificação
   if(!OrderSend(request, result))
   {
      Print("OrderModify error: ", GetLastError());
      return false;
   }
   
   // Verificar o resultado
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderModify failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   Print("Order modified successfully. Ticket: ", ticket);
   
   return true;
}

//+------------------------------------------------------------------+
//| Cancelar um pedido existente|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::CancelOrder(ulong ticket)
{
   // Preparar a solicitação
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_REMOVE;
   request.order = ticket;
   
   // Enviar a solicitação de cancelamento
   if(!OrderSend(request, result))
   {
      Print("OrderCancel error: ", GetLastError());
      return false;
   }
   
   // Verificar o resultado
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderCancel failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   Print("Order cancelled successfully. Ticket: ", ticket);
   
   return true;
}

//+------------------------------------------------------------------+
//| Atualizar o preço médio de execução|
//+------------------------------------------------------------------+
void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
{
   // Calcular o novo preço médio de execução
   if(m_executedVolume > 0.0)
   {
      // Média ponderada dos preços antigos e novos
      m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                           (m_executedVolume + volume);
   }
   else
   {
      // Primeira execução
      m_avgExecutionPrice = price;
   }
}
//+------------------------------------------------------------------+