English Русский 中文 Español Deutsch 日本語
preview
Algoritmos avançados de execução de ordens em MQL5: TWAP, VWAP e ordens Iceberg

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

MetaTrader 5Sistemas de negociação |
23 6
N Soumik
N Soumik
  1. Introdução
  2. Conhecendo os algoritmos de execução
  3. Implementação com recursos do MQL5
  4. Implementação do analisador de desempenho
  5. Comparação do desempenho dos algoritmos
  6. Integração dos algoritmos de execução com estratégias de trading
  7. Exemplos de integração
  8. Estratégia integrada
  9. Resultados do teste em histórico
  10. Conclusão



Introdução

Imagine que você está à margem de um salão de trading e sente o coração acelerar enquanto acompanha as variações de preço em tempo real. Um movimento errado, uma ordem grande demais, e sua vantagem desaparece em um piscar de olhos. Bem-vindo ao mundo em que a qualidade da execução não é apenas um bônus desejável, mas a arma secreta que separa os vencedores dos demais.

Durante décadas, os pesos-pesados institucionais usaram discretamente algoritmos complexos para fatiar, filtrar e distribuir suas ordens de forma discreta, evitando slippage e reduzindo o impacto de mercado. Agora, graças à flexibilidade do MQL5, esse mesmo arsenal de execução está ao alcance de qualquer trader de varejo ambicioso.

Qual é o problema, afinal?

Imagine a cena: você identifica uma excelente oportunidade e decide aumentar o tamanho da posição. Você coloca uma ordem a mercado pelo volume total, e acaba vendo o preço ceder sob o peso da sua própria operação. Em poucos segundos, sua entrada perfeita vira uma solução precária. Esse é o famoso arrasto do impacto de mercado, e ele cobra seu preço até nos ambientes mais líquidos.

Os algoritmos de execução são o seu antídoto. Ao dividir uma ordem grande em uma sequência de partes menores, distribuídas estrategicamente ao longo do tempo, eles suavizam sua pegada no livro de ofertas. O resultado? Menos slippage, execução mais precisa e uma melhora geral no preço médio de execução.

Das torres de marfim para a sua área de trabalho

"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.

Prepare-se para incorporar esse playbook à sua rotina de execução. O jogo está mudando, e com esses algoritmos no seu arsenal, você estará jogando para vencer.


Conhecendo os algoritmos de execução

Antes de mergulhar nos detalhes da implementação, é importante entender a teoria por trás de cada algoritmo de execução e compreender por que eles são eficazes em diferentes cenários de mercado.

  1. Preço médio ponderado pelo tempo (Time-Weighted Average Price, TWAP): TWAP é um algoritmo de execução simples que divide uma ordem grande em partes iguais e as envia em intervalos de tempo fixos durante um período definido. Seu objetivo é acompanhar o preço médio do instrumento nesse período.
    • Como funciona:

      • Envia ordens em intervalos regulares entre o início e o fim.
      • Normalmente usa ordens de mesmo tamanho, embora seja possível randomizar o tamanho das fatias.
      • Segue um cronograma predefinido, independentemente do movimento dos preços.
      • Distribui uniformemente o impacto de mercado ao longo do tempo, reduzindo o slippage ao mínimo.

    • Quando usar:

      • Você precisa de um preço médio de execução em um timeframe específico.
      • A liquidez permanece estável ao longo de todo o período de negociação.
      • Você tem um prazo fixo para executar sua ordem.
      • Você prefere uma abordagem simples e previsível.

  2. Preço médio ponderado pelo volume (Volume-Weighted Average Price, VWAP): VWAP aprimora o TWAP ao dimensionar as fatias da ordem de acordo com o volume esperado de acordo com o volume esperado. Em vez de partes iguais, ele envia operações maiores nos períodos de maior volume.
    • Como funciona:

      • Distribui o tamanho da ordem proporcionalmente aos padrões históricos de volume.
      • Analisa o histórico de volumes negociados para prever a distribuição futura do volume.
      • Em algumas implementações, pode se adaptar a mudanças de volume em tempo real.
      • Executa ordens com maior frequência nos períodos de pico de volume para reduzir o impacto de mercado.

    • Quando usar:

      • Seu desempenho é avaliado em relação ao VWAP.
      • O volume segue um padrão diário previsível.
      • Você opera em um mercado no qual a liquidez varia ao longo da sessão.
      • Você quer acompanhar o fluxo natural do mercado.

  3. Ordens Iceberg: As ordens do tipo Iceberg têm como objetivo ocultar o tamanho real de uma ordem grande. A qualquer momento, apenas uma pequena "ponta" fica visível; assim que ela é preenchida, a próxima parcela aparece.
    • Como funciona:

      • Exibe apenas uma parte da ordem total.
      • Libera novos blocos visíveis após a execução de cada parcela visível.
      • O tamanho visível pode ser fixo ou randomizado para reduzir a probabilidade de detecção.
      • Frequentemente são colocadas como ordens limitadas para melhor controle de preço.

    • Quando usar:

      • É necessário ocultar o tamanho total da sua ordem.
      • O mercado não é muito líquido, e operações grandes podem afetar os preços.
      • Você precisa manter a execução em um determinado nível de preço.
      • Você está preocupado com a possibilidade de outros traders detectarem sua ordem e praticarem front-running contra ela.


Implementação com recursos do MQL5

    Agora que entendemos a teoria por trás desses algoritmos de execução, vamos implementá-los em MQL5. Criaremos um framework modular e orientado a objetos que permitirá usar esses algoritmos separadamente ou combiná-los em um único sistema de execução.

    Classe base: CExecutionAlgorithm

    Começaremos definindo uma classe base que fornece uma funcionalidade comum a todos os algoritmos de execução:

    //+------------------------------------------------------------------+
    //| Base class for all execution algorithms                          |
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;              // Symbol to trade
       double            m_totalVolume;         // Total volume to execute
       double            m_executedVolume;      // Volume already executed
       double            m_remainingVolume;     // Volume remaining to execute
       datetime          m_startTime;           // Start time for execution
       datetime          m_endTime;             // End time for execution
       bool              m_isActive;            // Flag indicating if the algorithm is active
       int               m_totalOrders;         // Total number of orders placed
       int               m_filledOrders;        // Number of filled orders
       double            m_avgExecutionPrice;   // Average execution price
       double            m_executionValue;      // Total value of executed orders
       int               m_slippage;            // Allowed slippage in points
       
    public:
       // Constructor
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage = 3);
       
       // Destructor
       virtual ~CExecutionAlgorithm();
       
       // Common methods
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate();
       
       // Utility methods
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Getters
       string            GetSymbol() const { return m_symbol; }
       double            GetTotalVolume() const { return m_totalVolume; }
       double            GetExecutedVolume() const { return m_executedVolume; }
       double            GetRemainingVolume() const { return m_remainingVolume; }
       datetime          GetStartTime() const { return m_startTime; }
       datetime          GetEndTime() const { return m_endTime; }
       bool              IsActive() const { return m_isActive; }
       int               GetTotalOrders() const { return m_totalOrders; }
       int               GetFilledOrders() const { return m_filledOrders; }
       double            GetAverageExecutionPrice() const { return m_avgExecutionPrice; }
    };
    

    O método PlaceOrder é especialmente importante, pois trata a execução efetiva da ordem e atualiza o acompanhamento do volume:

    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price)
    {
       // Prepare the trade request
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volume;
       m_remainingVolume -= volume;
       UpdateAverageExecutionPrice(price, volume);
       
       // Store the order ticket for future reference
       ulong ticket = result.order;
       
       return true;
    }
    

    Essa função cria e envia uma ordem a mercado zerando MqlTradeRequest e MqlTradeResult, preenchendo o símbolo, o volume, o tipo de ordem, o preço, o slippage e o magic number, e então chamando OrderSend. Se o envio falhar ou se o código de retorno da corretora não for TRADE_RETCODE_DONE, ela registra o erro e retorna false. Em caso de sucesso, atualiza os contadores internos, incluindo o os contadores internos, incluindo a contagem total, a quantidade de execuções, o volume executado e o volume restante, recalcula o preço médio, armazena o ticket e retorna true.

    Implementação do TWAP

    O algoritmo TWAP divide o período de execução em intervalos de tempo iguais e envia ordens de tamanho igual ou aleatório, em cada intervalo:

    //+------------------------------------------------------------------+
    //| Time-Weighted Average Price (TWAP) Algorithm                     |
    //+------------------------------------------------------------------+
    class CTWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_intervalVolume;      // Volume per interval
       bool              m_useRandomization;    // Whether to randomize order sizes
       double            m_randomizationFactor; // Factor for randomization (0-1)
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
             double randomizationFactor = 0.2, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // TWAP specific methods
       void              CalculateIntervalVolume();
       datetime          CalculateNextExecutionTime();
       double            GetRandomizedVolume(double baseVolume);
       bool              IsTimeToExecute();
    };
    

    Método-chave: CalculateNextExecutionTime

    Esse método cuida da distribuição adequada das ordens ao longo do tempo, com um atraso inicial para a primeira ordem:
    datetime CTWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
       // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("TWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("TWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Esse método divide a janela entre m_startTime e m_endTime em segmentos iguais, definidos por m_intervals, e retorna o momento em que o momento em que a próxima operação deve ser disparada: na primeira chamada, esse momento é simplesmente m_startTime + m_initialDelay; em cada chamada posterior, é TimeCurrent() + um intervalo em segundos, mas nunca além de m_endTime.

    Método Execute:O método Execute verifica se chegou a hora de colocar uma ordem e trata o envio efetivo da ordem:

    bool CTWAP::Execute()
    {
       if(!m_isActive)
          return false;
       
       // Check if it's time to execute the next order
       if(!IsTimeToExecute())
          return true; // Not time yet
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomization ? 
                               GetRandomizedVolume(m_intervalVolume) : 
                               m_intervalVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       // Get current market price
       double price = 0.0;
       if(m_orderType == ORDER_TYPE_BUY)
          price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
       else
          price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          
       Print("TWAP: Placing order for interval ", m_currentInterval,
             ", Volume: ", DoubleToString(volumeToExecute, 2),
             ", Price: ", DoubleToString(price, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("TWAP: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("TWAP: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volumeToExecute;
       m_remainingVolume -= volumeToExecute;
       
       // Update interval counter
       m_currentInterval++;
       m_firstOrderPlaced = true;
       
       // Calculate the time for the next execution
       if(m_currentInterval < m_intervals && m_remainingVolume > 0)
          m_nextExecutionTime = CalculateNextExecutionTime();
       else
          m_isActive = false; // All intervals completed or no volume left
          
       Print("TWAP: Executed ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(price, _Digits), 
             ". Remaining: ", DoubleToString(m_remainingVolume, 2),
             ", Next execution: ", TimeToString(m_nextExecutionTime));
             
       return true;
    }
    

    Esse método Execute gerencia um único fragmento da execução do TWAP. Primeiro, ele interrompe a execução se a estratégia não estiver ativa ou se ainda não tiver chegado o momento de operar. Em seguida, seleciona uma fatia fixa ou aleatória do volume restante, sem nunca exceder o que ainda resta, e então busca o Ask atual para compra ou o Bid atual para venda. Ele registra o intervalo, o volume e o preço, cria MqlTradeRequest com o símbolo, volume, tipo, preço, slippage e magic number, e então chama OrderSend. Se o envio falhar ou se a corretora retornar qualquer valor diferente de TRADE_RETCODE_DONE, ele exibe o erro e retorna false.

    Em caso de sucesso, incrementa os contadores de ordens, ajusta o volume executado e o volume restante, incrementa o contador de intervalos, marca que a primeira ordem foi enviada e então agenda o horário da próxima execução ou desativa a estratégia caso os intervalos ou o volume tenham se esgotado. Por fim, registra o evento ocorrido e retorna true.

    Implementação do VWAP

    O algoritmo VWAP é parecido com o TWAP, mas distribui as fatias da ordem com base em padrões históricos de volume:
    //+------------------------------------------------------------------+
    //| Volume-Weighted Average Price (VWAP) Algorithm                   |
    //+------------------------------------------------------------------+
    class CVWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_volumeProfile[];     // Historical volume profile
       double            m_intervalVolumes[];   // Volume per interval based on profile
       bool              m_adaptiveMode;        // Whether to adapt to real-time volume
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       int               m_historyDays;         // Number of days to analyze for volume profile
       bool              m_profileLoaded;       // Flag indicating if profile was loaded
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
             bool adaptiveMode = true, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // VWAP specific methods
       bool              LoadVolumeProfile();
       void              CalculateIntervalVolumes();
       void              AdjustToRealTimeVolume();
       datetime          CalculateNextExecutionTime();
       double            GetCurrentVWAP();
       bool              IsTimeToExecute();
    };
    
    Assim como no caso do TWAP, o VWAP também implementa o método CalculateNextExecutionTime para garantir o intervalo adequado entre as ordens:
    datetime CVWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
          // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("VWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Implementação das ordens Iceberg

    As ordens do tipo Iceberg ocultam o tamanho real da ordem, expondo ao mercado apenas uma pequena parte dela a qualquer momento:

    //+------------------------------------------------------------------+
    //| Iceberg Order Implementation                                     |
    //+------------------------------------------------------------------+
    class CIcebergOrder : public CExecutionAlgorithm
    {
    private:
       double            m_visibleVolume;          // Visible portion of the order
       double            m_minVisibleVolume;       // Minimum visible volume
       double            m_maxVisibleVolume;       // Maximum visible volume
       bool              m_useRandomVisibleVolume; // Whether to randomize visible volume
       int               m_orderPlacementDelay;    // Delay between order placements (ms)
       bool              m_avoidRoundNumbers;      // Whether to avoid round numbers in price
       double            m_limitPrice;             // Limit price for the orders
       ulong             m_currentOrderTicket;     // Current active order ticket
       ENUM_ORDER_TYPE   m_orderType;              // Order type (buy or sell)
       bool              m_orderActive;            // Flag indicating if an order is currently active
       int               m_priceDeviation;         // Price deviation to avoid round numbers (in points)
       datetime          m_lastCheckTime;          // Last time order status was checked
       int               m_checkInterval;          // How often to check order status (seconds)
       int               m_maxOrderLifetime;       // Maximum lifetime for an order in seconds
       datetime          m_orderPlacementTime;     // When the current order was placed
       
    public:
       // Constructor
       CIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                     double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                     bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                     bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // Iceberg specific methods
       double            GetRandomVisibleVolume();
       double            AdjustPriceToAvoidRoundNumbers(double price);
       bool              CheckAndReplaceOrder();
       bool              IsOrderFilled(ulong ticket);
       bool              IsOrderPartiallyFilled(ulong ticket, double &filledVolume);
       bool              IsOrderCancelled(ulong ticket);
       bool              IsOrderExpired(ulong ticket);
       bool              IsOrderTimeout();
       ulong             GetCurrentOrderTicket() { return m_currentOrderTicket; }
       bool              IsOrderActive() { return m_orderActive; }
    };
    
    O método Execute envia uma nova parcela visível da ordem:
    bool CIcebergOrder::Execute()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Execute called but algorithm is not active");
          return false;
       }
          
       // If an order is already active, check its status
       if(m_orderActive)
       {
          Print("Iceberg: Execute called with active order ", m_currentOrderTicket);
          return CheckAndReplaceOrder();
       }
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomVisibleVolume ? 
                               GetRandomVisibleVolume() : 
                               m_visibleVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       Print("Iceberg: Placing order for ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(m_limitPrice, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_PENDING;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = m_limitPrice;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("Iceberg: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("Iceberg: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Store the order ticket
       m_currentOrderTicket = result.order;
       m_orderActive = true;
       m_orderPlacementTime = TimeCurrent();
       
       Print("Iceberg: Order placed successfully. Ticket: ", m_currentOrderTicket,
             ", Volume: ", DoubleToString(volumeToExecute, 2), 
             ", Remaining: ", DoubleToString(m_remainingVolume, 2));
             
       return true;
    }
    

    Ao ser iniciado, Execute verifica primeiro se o algoritmo está ativo. Se já houver uma ordem filha Iceberg ativa, ele chama CheckAndReplaceOrder() para verificar se é necessário cancelá-la ou repor a parcela visível. Caso contrário, seleciona uma fatia visível, fixa ou aleatória, limita essa fatia ao volume restante e registra o tamanho e o preço.

    Em seguida, cria uma requisição de ordem pendente (TRADE_ACTION_PENDING) com o símbolo, o volume, o preço limite, o slippage e o magic number, e chama OrderSend. Em caso de erro ou se o código de retorno não indicar TRADE_RETCODE_DONE, ele registra o problema e retorna false; em caso de sucesso, armazena o novo ticket, marca a ordem como ativa, registra o horário de envio, grava os dados e retorna true.

    O método Update inclui a detecção de timeout da ordem para garantir que as ordens não permaneçam ativas indefinidamente:

    bool CIcebergOrder::Update()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Update called but algorithm is not active");
          return false;
       }
          
       // Check if all volume has been executed
       if(m_remainingVolume <= 0)
       {
          Print("Iceberg: All volume executed. Terminating algorithm.");
          return Terminate();
       }
       
       // Check if it's time to check order status
       datetime currentTime = TimeCurrent();
       if(currentTime >= m_lastCheckTime + m_checkInterval)
       {
          m_lastCheckTime = currentTime;
          
          // Log current market conditions
          double currentBid = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          double currentAsk = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          
          Print("Iceberg: Market update - Bid: ", DoubleToString(currentBid, _Digits),
                ", Ask: ", DoubleToString(currentAsk, _Digits),
                ", Limit Price: ", DoubleToString(m_limitPrice, _Digits));
          
          // If an order is active, check its status
          if(m_orderActive)
          {
             // Check if the order has been active too long
             if(IsOrderTimeout())
             {
                Print("Iceberg: Order ", m_currentOrderTicket, " has timed out. Replacing it.");
                
                // Cancel the current order
                if(!CancelOrder(m_currentOrderTicket))
                {
                   Print("Iceberg: Failed to cancel timed out order ", m_currentOrderTicket);
                }
                
                // Reset order tracking
                m_orderActive = false;
                m_currentOrderTicket = 0;
                
                // Place a new order after a delay
                Sleep(m_orderPlacementDelay);
                return Execute();
             }
             
             return CheckAndReplaceOrder();
          }
          else
          {
             // If no order is active, execute a new one
             Print("Iceberg: No active order, executing new order");
             return Execute();
          }
       }
       
       return true;
    }
    

    Update consulta periodicamente o mercado e o status das ordens em intervalos definidos por m_checkInterval. Se todo o volume tiver sido esgotado, ele encerra a execução. Caso contrário, assim que chega o momento da verificação, registra os preços Bid/Ask atuais e o preço limite. Se houver uma ordem ativa, ela é verificada quanto ao timeout: se a ordem tiver expirado, ela é cancelada, o estado é redefinido, o algoritmo entra no modo m_orderPlacementDelay e Execute é chamado novamente para colocar uma nova fatia; se o timeout ainda não tiver expirado, a verificação é delegada a CheckAndReplaceOrder(). Se não houver ordem ativa, ele simplesmente chama Execute para enviar a próxima parcela visível.


    Implementação do analisador de desempenho

    Nossa classe de analisador de desempenho acompanha essas métricas e fornece métodos para analisar e comparar o funcionamento dos algoritmos:
    //+------------------------------------------------------------------+
    //| Performance Analyzer for Execution Algorithms                    |
    //+------------------------------------------------------------------+
    class CPerformanceAnalyzer
    {
    private:
       string            m_symbol;              // Symbol being analyzed
       datetime          m_startTime;           // Analysis start time
       datetime          m_endTime;             // Analysis end time
       double            m_decisionPrice;       // Price at decision time
       double            m_avgExecutionPrice;   // Average execution price
       double            m_totalVolume;         // Total volume executed
       double            m_implementationShortfall; // Implementation shortfall
       double            m_marketImpact;        // Estimated market impact
       double            m_slippage;            // Average slippage
       int               m_executionTime;       // Total execution time in seconds
       double            m_priceImprovement;    // Total price improvement
       
    public:
       // Constructor
       CPerformanceAnalyzer(string symbol, double decisionPrice);
       
       // Analysis methods
       void              RecordExecution(datetime time, double price, double volume);
       void              CalculateMetrics();
       void              CompareAlgorithms(CPerformanceAnalyzer &other);
       
       // Reporting methods
       void              PrintReport();
       void              SaveReportToFile(string filename);
       
       // Getters
       double            GetImplementationShortfall() const { return m_implementationShortfall; }
       double            GetMarketImpact() const { return m_marketImpact; }
       double            GetSlippage() const { return m_slippage; }
       int               GetExecutionTime() const { return m_executionTime; }
       double            GetPriceImprovement() const { return m_priceImprovement; }
    };
    

    CPerformanceAnalyzer reúne todos os seus indicadores pós-trade em um só lugar. Ao instanciá-lo, você atribui a ele o símbolo e o preço de referência no momento da decisão; como ponto inicial, ele marca o horário atual. À medida que cada ordem filha é executada, você chama RecordExecution(tempo, preço, volume), que atualiza os totais acumulados: volume acumulado, preço médio ponderado de execução e timestamps. Após a conclusão da estratégia, ou periodicamente, você chama CalculateMetrics(), que calcula:

    • Déficit de execução, isto é, a diferença no P&L entre o preço da decisão e a execução real,
    • Slippage médio em comparação com os preços cotados,
    • Impacto estimado da sua presença no mercado,
    • Tempo total de execução, fim menos início,
    • Melhoria de preço, se houver, em comparação com os benchmarks.

    Você pode até comparar duas rodadas com CompareAlgorithms(otherAnalyzer) para ver qual estratégia apresentou os melhores resultados. Por fim, PrintReport() imprime as principais estatísticas no log para consulta rápida, enquanto SaveReportToFile(filename) permite salvar o relatório completo em um armazenamento externo. Métodos acessores leves dão acesso a cada métrica para dashboards personalizados ou análises posteriores.


    Comparação do desempenho dos algoritmos

    Diferentes condições de mercado favorecem diferentes algoritmos de execução. Veja uma comparação geral, isto é, uma regra prática:
    1. TWAP:
      • Mais indicado para: Mercados estáveis com liquidez constante
      • Vantagens: Padrão de execução simples e previsível
      • Desvantagens: Não se adapta a mudanças nas condições de mercado
    2. VWAP:
      • Mais indicado para: Mercados com padrões de volume previsíveis
      • Vantagens: Acompanha o ritmo natural do mercado e muitas vezes alcança preços melhores
      • Desvantagens: Exige histórico suficiente de volumes negociados e tem implementação mais complexa
    3. Ordens Iceberg:
      • Mais indicado para: Mercados menos líquidos ou situações em que a sensibilidade dos preços é alta
      • Vantagens: Minimiza o impacto de mercado e preserva o controle sobre o preço
      • Desvantagens: O tempo de execução pode ser imprevisível, com risco de execução parcial


    Integração dos algoritmos de execução com estratégias de trading

    A verdadeira força desses algoritmos de execução aparece quando eles são integrados a estratégias de trading. Esta seção mostra como integrar nossos algoritmos de execução a sistemas de trading completos.

    Gerenciador

    Para simplificar a integração, criaremos uma classe gerenciadora (Execution Manager) que servirá como fachada para todos os nossos algoritmos de execução:

    //+------------------------------------------------------------------+
    //| Execution Manager - Facade for all execution algorithms          |
    //+------------------------------------------------------------------+
    class CExecutionManager
    {
    private:
       CExecutionAlgorithm* m_algorithm;       // Current execution algorithm
       CPerformanceAnalyzer* m_analyzer;       // Performance analyzer
       
    public:
       // Constructor
       CExecutionManager();
       
       // Destructor
       ~CExecutionManager();
       
       // Algorithm creation methods
       bool              CreateTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
                                   double randomizationFactor = 0.2, int slippage = 3);
                                   
       bool              CreateVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
                                   bool adaptiveMode = true, int slippage = 3);
                                   
       bool              CreateIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                                           double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                                           bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                                           bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Execution methods
       bool              Initialize();
       bool              Execute();
       bool              Update();
       bool              Terminate();
       
       // Performance analysis
       void              EnablePerformanceAnalysis(double decisionPrice);
       void              PrintPerformanceReport();
       
       // Getters
       CExecutionAlgorithm* GetAlgorithm() { return m_algorithm; }
    };
    

    CExecutionManager atua como uma fachada simples para qualquer um dos seus algoritmos de execução e os reúne em um único fluxo de trading. Internamente, ele armazena um ponteiro para o CExecutionAlgorithm selecionado no momento, TWAP, VWAP ou Iceberg, além de um CPerformanceAnalyzer para acompanhar quão bem suas ordens estão sendo executadas.

    Você escolhe sua estratégia chamando um dos métodos Create…, passando o símbolo, o volume total, os horários de início/fim (para TWAP/VWAP), a quantidade de intervalos ou os tamanhos das fatias, o tipo de ordem e quaisquer parâmetros específicos do algoritmo, como randomização, janela histórica, preço limite etc. Depois de criado, você conduz o algoritmo pelo ciclo de vida padrão:

    1. Initialize() configura os estados ou dados necessários.
    2. Execute() dispara a próxima fatia ou ordem filha.
    3. Update() consulta execuções de ordens, dados de mercado ou timeouts.
    4. Terminate() faz a limpeza assim que todas as ordens forem executadas ou quando você quiser parar.

    Se você ativar a análise de desempenho com EnablePerformanceAnalysis(), o gerenciador registrará os preços de execução em comparação com o benchmark no momento da decisão, e você poderá gerar um relatório resumido de P&L e slippage com PrintPerformanceReport(). Você sempre pode obter o objeto subjacente do algoritmo com GetAlgorithm() para inspeção ou medição personalizada.


    Exemplos de integração

    Abaixo estão exemplos de como integrar nossos algoritmos de execução a diferentes tipos de estratégias de trading:

    1. Estratégia de seguimento de tendência com execução TWAP.

      //+------------------------------------------------------------------+
      //| Trend-Following Strategy with TWAP Execution                     |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int maPeriodFast = 20;
         int maPeriodSlow = 50;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double maFast = iMA(Symbol(), PERIOD_CURRENT, maPeriodFast, 0, MODE_SMA, PRICE_CLOSE, 0);
         double maSlow = iMA(Symbol(), PERIOD_CURRENT, maPeriodSlow, 0, MODE_SMA, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Fast MA crosses above Slow MA
            if(maFast > maSlow)
            {
               // Create TWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateTWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy signal detected. Starting TWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("TWAP execution completed.");
               }
            }
         }
      }
      
    2. Estratégia de retorno à média com execução VWAP.

      //+------------------------------------------------------------------+
      //| Mean-Reversion Strategy with VWAP Execution                      |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int rsiPeriod = 14;
         int rsiOversold = 30;
         int rsiOverbought = 70;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double rsi = iRSI(Symbol(), PERIOD_CURRENT, rsiPeriod, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static bool isLong = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: RSI oversold
            if(rsi < rsiOversold)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = true;
                  Print("Buy signal detected. Starting VWAP execution.");
               }
            }
            // Sell signal: RSI overbought
            else if(rsi > rsiOverbought)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_SELL))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = false;
                  Print("Sell signal detected. Starting VWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("VWAP execution completed.");
               }
            }
            
            // Check for exit conditions
            if(isLong && rsi > rsiOverbought)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
            else if(!isLong && rsi < rsiOversold)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
         }
      }
      
    3. Estratégia de rompimento com ordens Iceberg.
      //+------------------------------------------------------------------+
      //| Breakout Strategy with Iceberg Orders                            |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int channelPeriod = 20;
         double volume = 1.0;
         double visibleVolume = 0.1;
         
         // Calculate indicators
         double upperChannel = iHigh(Symbol(), PERIOD_CURRENT, iHighest(Symbol(), PERIOD_CURRENT, MODE_HIGH, channelPeriod, 1));
         double lowerChannel = iLow(Symbol(), PERIOD_CURRENT, iLowest(Symbol(), PERIOD_CURRENT, MODE_LOW, channelPeriod, 1));
         
         double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Price breaks above upper channel
            if(currentPrice > upperChannel)
            {
               // Create Iceberg Order
               double limitPrice = upperChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_BUY,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy breakout detected. Starting Iceberg Order execution.");
               }
            }
            // Sell signal: Price breaks below lower channel
            else if(currentPrice < lowerChannel)
            {
               // Create Iceberg Order
               double limitPrice = lowerChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_SELL,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Sell breakout detected. Starting Iceberg Order execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("Iceberg Order execution completed.");
               }
            }
         }
      }
      

    Em todos os três exemplos, a integração segue o mesmo padrão de alto nível:

    1. Manutenção do estado

      • A variável booleana inPosition acompanha se, no momento, há uma ordem em execução.
      • O gerenciador estático CExecutionManager persiste entre ticks e gerencia o ciclo de vida do algoritmo escolhido.

    2. Lógica de entrada

      • Após o seu sinal de entrada (cruzamento de médias móveis, limite de RSI ou rompimento de canal), chame o método de criação correspondente em executionManager (TWAP, VWAP ou Iceberg), passando o símbolo, o volume total, a janela de tempo ou o preço limite, os parâmetros das fatias e o tipo de ordem.
      • Se o objeto for criado com sucesso, chame imediatamente executionManager.Initialize(), defina inPosition=true e registre o início do fluxo de execução.

    3. Execução contínua

      • Enquanto inPosition for true, cada OnTick() chama executionManager.Update().
      • Dentro de Update(), o gerenciador chamará Execute() conforme necessário, consultará fills, tratará timeouts ou atualizações de mercado e agendará a próxima fatia ou cancelará/substituirá ordens filhas no caso do Iceberg.

    4. Conclusão e saída

      • Após cada atualização, verifique executionManager.GetAlgorithm()->IsActive(). Assim que ele retornar false (quando todos os intervalos tiverem sido concluídos ou o volume tiver sido esgotado), defina inPosition=false e registre no log que a execução foi concluída.
      • No exemplo de retorno à média com VWAP, há uma verificação adicional de saída: se o preço reverter e ultrapassar o limite de RSI no meio da execução, chame executionManager.Terminate() para interromper antecipadamente as fatias restantes.

    As regras para cada uma delas são as seguintes:
    1. Seguimento de tendência + TWAP
      Entrada: O cruzamento da média móvel rápida acima da média móvel lenta aciona a entrada
      Execução: Divide sua compra de 1 lote em 5 partes iguais ao longo da próxima hora

    2. Retorno à média + VWAP
      Entrada: RSI < 30 para operações de compra, > 70 para operações de venda
      Execução: Distribui 1 lote em 5 partes ao longo de 1 hora, conforme o perfil histórico de volume.
      Saída antecipada: Se o sinal mudar, por exemplo, RSI > 70 durante uma operação de compra, executionManager.Terminate() interrompe as partes restantes

    3. Rompimento + Iceberg
      Entrada: O preço rompe a máxima do canal (compra) ou a mínima do canal (venda).
      Execução: Coloca uma ordem pendente limitada no nível do preço de rompimento, tornando visível apenas ~0,1 lote por vez e repondo essa parcela até que 1 lote inteiro seja executado.

    Ao trocar entre CreateTWAP, CreateVWAP e CreateIcebergOrder, você conecta qualquer algoritmo de execução à sua lógica de sinal sem duplicar o lógica padrão de gerenciamento de ordens.


    Estratégia integrada

    Código completo:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    
    // Input parameters
    input int FastMA = 20;                  // Fast moving average period
    input int SlowMA = 50;                  // Slow moving average period
    input double TradingVolume = 0.1;       // Trading volume
    input bool UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int g_maHandle1 = INVALID_HANDLE;
    int g_maHandle2 = INVALID_HANDLE;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
    {
       // Initialize execution manager
       g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
       
       // Initialize indicators
       g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
       g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
       
       if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE)
       {
          Print("Failed to create indicator handles");
          return INIT_FAILED;
       }
       
       return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
       // Clean up
       if(g_executionManager != NULL)
       {
          delete g_executionManager;
          g_executionManager = NULL;
       }
       
       // Release indicator handles
       IndicatorRelease(g_maHandle1);
       IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
    {
       // Update execution algorithms
       if(g_executionManager != NULL)
          g_executionManager.UpdateAlgorithms();
       
       // Only process at bar open
       if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
          return;
          
       // Get indicator values
       double fastMA[2], slowMA[2];
       
       if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
          CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0)
       {
          Print("Failed to copy indicator buffers");
          return;
       }
       
       // Check for trend signals
       bool buySignal = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
       bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
       
       // Execute signals using the execution manager
       if(buySignal)
       {
          Print("Buy signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_BUY, true);
          }
       }
       else if(sellSignal)
       {
          Print("Sell signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_SELL, true);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    O EA IntegratedStrategy.mq5 começa declarando seus metadados, copyright, link e versão, e incluindo os cabeçalhos de todas as nossas classes de algoritmos de execução e do analisador de desempenho. Em seguida, define quatro entradas configuráveis pelo usuário: os períodos das médias móveis simples rápida e lenta, o volume total de negociação por sinal e uma flag booleana para alternar para a execução "adaptativa", em que o gerenciador decide se deve usar TWAP, VWAP ou Iceberg. Também são declarados um ponteiro global para CExecutionManager e dois handles de indicador, para que sejam preservados entre os ticks.

    Em OnInit(), instanciamos o gerenciador de execução, passando a ele o símbolo atual, o máximo de três algoritmos paralelos e nossa flag adaptativa; em seguida, criamos dois handles de indicador SMA. Se qualquer um dos handles falhar, a inicialização é interrompida. OnDeinit() apenas faz a limpeza, excluindo o gerenciador e liberando os handles dos indicadores, garantindo que não haja vazamentos de memória ou de handles quando o EA for removido ou quando a plataforma for encerrada.

    A lógica principal fica em OnTick(). Primeiro, chamamos UpdateAlgorithms() no gerenciador de execução para que quaisquer ordens filhas existentes, fatias TWAP, intervalos VWAP ou ramificações Iceberg, sejam processadas, canceladas ou reabastecidas conforme necessário. Em seguida, aguardamos uma nova barra, ignorando-a se o volume de ticks ainda estiver aumentando. Após a abertura da barra, extraímos os dois últimos valores da SMA para os períodos rápido e lento. O cruzamento de baixo para cima gera um sinal de compra; o cruzamento inverso gera um sinal de venda.

    Se a execução adaptativa estiver ativada, passamos o sinal e o volume para g_executionManager.ExecuteSignal(), para que ele escolha o algoritmo adequado. Caso contrário, iniciamos manualmente uma instância TWAP para uma hora e seis fatias. Esse padrão separa claramente a lógica de entrada, detecção de tendência, da lógica de gerenciamento de ordens, permitindo que a mesma fachada gerencie vários estilos de execução sem duplicar o padrão.

    Após ativar o Take Profit, o código muda para:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    #include <Trade\Trade.mqh>
    
    // Input parameters
    input int    FastMA              = 20;    // Fast moving average period
    input int    SlowMA              = 50;    // Slow moving average period
    input double TradingVolume       = 0.1;   // Trading volume
    input bool   UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    input double EquityTPPercent     = 10.0;  // Equity Take Profit in percent
    input double EquitySLPercent     = 5.0;   // Equity Stop Loss in percent
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int                g_maHandle1       = INVALID_HANDLE;
    int                g_maHandle2       = INVALID_HANDLE;
    double             g_initialEquity   = 0.0;
    CTrade             trade;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit() {
        // Record initial equity
        g_initialEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    
        // Initialize execution manager
        g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
    
        // Initialize indicators
        g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
        g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
    
        if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE) {
            Print("Failed to create indicator handles");
            return INIT_FAILED;
        }
    
        return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason) {
        // Clean up
        if(g_executionManager != NULL) {
            delete g_executionManager;
            g_executionManager = NULL;
        }
    
        // Release indicator handles
        IndicatorRelease(g_maHandle1);
        IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Check equity-based TP and SL, then reset baseline                |
    //+------------------------------------------------------------------+
    void CheckEquityTPandSL() {
        double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
        double tpEquity      = g_initialEquity * (1.0 + EquityTPPercent / 100.0);
        double slEquity      = g_initialEquity * (1.0 - EquitySLPercent / 100.0);
    
        if(currentEquity >= tpEquity) {
            Print("Equity Take Profit reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        } else if(currentEquity <= slEquity) {
            Print("Equity Stop Loss reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        }
    }
    
    //+------------------------------------------------------------------+
    //| Close all open positions                                         |
    //+------------------------------------------------------------------+
    void CloseAllPositions() {
        CPositionInfo  m_position;
        CTrade m_trade;
        for(int i = PositionsTotal() - 1; i >= 0; i--) // loop all Open Positions
            if(m_position.SelectByIndex(i)) { // select a position
                m_trade.PositionClose(m_position.Ticket()); // then delete it --period
            }
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick() {
        // Check and reset equity thresholds
        CheckEquityTPandSL();
    
        // Update execution algorithms
        if(g_executionManager != NULL)
            g_executionManager.UpdateAlgorithms();
    
        // Only process at bar open
        if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
            return;
    
        // Get indicator values
        double fastMA[2], slowMA[2];
    
        if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
                CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0) {
            Print("Failed to copy indicator buffers");
            return;
        }
    
        // Check for trend signals
        bool buySignal  = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
        bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
    
        // Execute signals using the execution manager
        if(buySignal) {
            Print("Buy signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_BUY, true);
            }
        } else if(sellSignal) {
            Print("Sell signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_SELL, true);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    Resultados do teste em histórico

    1. Curvas de equity e saldo

    A escada verde "Balance" mostra o saldo da sua conta sempre que o EA fecha uma posição; a linha azul "Equity" suaviza os lucros e perdas não realizados entre as operações. Podemos observar uma tendência clara de alta de janeiro ao início de março, com alguns recuos, cada recuo chega a um máximo de cerca de 10-16% antes que a próxima sequência de ganhos recupere o crescimento. Esse padrão sugere que o sistema prospera em condições de tendência, mas ainda passa por quedas de capital toleráveis.

    2. Volume e uso de risco

    Na parte inferior, os triângulos "Carga sobre o depósito" (Deposit Load) diminuem gradualmente ao longo do tempo, isto é, o tamanho da sua posição como percentual do capital. Ele começa em torno de 10% do seu saldo e diminui à medida que o seu capital cresce, com tamanho de volume fixo, ou seja, nosso risco por operação efetivamente diminui conforme a conta cresce. É por isso que os drawdowns permanecem proporcionalmente semelhantes, mesmo que seu capital em dólares aumente.

    3. Principais indicadores de rentabilidade

    • Depósito inicial: $1 000

    • Lucro líquido: + $703, rentabilidade de 70% em ~2 meses

    • Fator de lucro: 2,34, você ganha 2,34 dólares para cada 1 dólar perdido

    • Lucro esperado: em média 2,34 dólares por trade

    • Índice de Sharpe: 5,47, muito alto, alta rentabilidade ajustada ao risco

    Esses números mostram que a estratégia não é apenas lucrativa, mas também oferece uma margem substancial acima da própria volatilidade.

    4. Drawdown e recuperação

    • Drawdown máximo do saldo: 156 pontos ou 9.99%

    • Drawdown máximo da equity: 228 pontos ou 15.89%

    • Fator de recuperação: 3.08, lucro líquido ÷ drawdown máximo

    Um fator de recuperação acima de 2 geralmente é considerado bom, portanto, com 3,08, você obtém um lucro três vezes maior que sua maior perda.

    5. Distribuição dos trades

    • Total de trades: 300, 600 deals, portanto cada entrada+saída conta como dois

    • Taxa de acerto: 76%, 228 ganhos contra 72 perdas

    • Ganho médio: $5,39

    • Perda média: – $7,31

    Embora sua taxa de acerto e seu fator de lucro sejam altos, observe que, em média, seus trades perdedores são maiores que os vencedores. Esse é um ponto que merece atenção caso as condições de mercado mudem.

    6. Sequências e consistência

    • Maior sequência de trades vencedores: 87 trades, + $302

    • Maior sequência de trades perdedores: 23 trades, – $156

    • Sequência vencedora média: 57 trades

    • Sequência perdedora média: 18 trades

    Sequências vencedoras longas impulsionam o crescimento, enquanto a maior sequência de perdas ainda custa apenas cerca de 15% do capital.


    Conclusão

    Imagine que você é um trader individual em uma arena de mercado lotada, onde cada tick importa e cada preço de execução antecipa lucro ou prejuízo. Ao incorporar ordens TWAP, VWAP e Iceberg ao seu conjunto de ferramentas, você deixa de apenas reagir às oscilações de preço e passa a regê-las. Esses algoritmos de nível institucional, antes reservados à elite, agora estão ao seu alcance, fatiando a liquidez como um laser e transformando livros de ofertas caóticos em oportunidades.

    O TWAP se torna seu metrônomo constante, ajustando o tamanho de forma uniforme dentro de um intervalo definido, ideal quando a corrente está tranquila e você só precisa de uma travessia suave. O VWAP transforma você em um trader experiente, acompanhando os momentos de maior intensidade do dia e sentindo o pulso do mercado. E, quando você precisa ocultar suas intenções, as ordens Iceberg escondem seu tamanho real sob a superfície, revelando apenas o necessário para executar a ordem sem assustar os grandes players.

    Mas não se trata apenas de técnicas isoladas. Com nosso framework MQL5 modular, você pode integrá-las a qualquer estratégia, seja seguimento de tendência, retorno à média ou busca por rompimentos, alternando facilmente para uma nova abordagem. A fachada unificada ExecutionManager permite trocar, combinar ou até sobrepor algoritmos durante o trading, enquanto PerformanceAnalyzer mantém o placar com olhos de águia, medindo slippage, déficit e impacto de mercado com precisão até o último pip.

    O que vem a seguir? Pense na execução como um organismo vivo que se adapta. Permita que seu TWAP aprenda com os picos de volatilidade. Direcione as fatias do VWAP para os poços de liquidez mais profundos. Ensine seu Iceberg a sentir onde os predadores estão à espreita e a se esconder mais fundo. E por que parar por aí? Incorpore machine learning para prever o microssegundo ideal de disparo ou combine tipos de ordens em híbridos personalizados que se ajustem à sua vantagem única.

    O mundo do trading nunca fica parado, e a execução das suas ordens também não deve parar. Mergulhe, experimente com ousadia e transforme cada fatia, cada execução, em uma vantagem calculada. Sua vantagem está esperando no código.

    Para facilitar, segue uma visão geral rápida dos arquivos incluídos neste artigo:

    Nome do arquivo Descrição
    ExecutionAlgorithm.mqh Classe base para todos os algoritmos de execução 
    TWAP.mqh Implementação do preço médio ponderado pelo tempo
    VWAP.mqh Implementação do preço médio ponderado pelo volume
    IcebergOrder.mqh Implementação das ordens Iceberg
    PerformanceAnalyzer.mqh Ferramentas para análise do desempenho da execução
    ExecutionManager.mqh Fachada para integração fácil com estratégias de trading
    IntegratedStrategy.mq5
    Exemplo de EA que demonstra a integração com uma estratégia de trading
    IntegratedStrategy - Take Profit.mq5 Exemplo de EA que demonstra a integração com uma estratégia de trading com Take Profit e Stop Loss em percentual do saldo da conta
    Ao incorporar esses algoritmos avançados de execução ao seu conjunto de ferramentas de trading, você dá um passo importante rumo a uma negociação mais profissional e eficiente. Seja para minimizar o impacto de operações grandes, melhorar os preços médios de execução ou simplesmente aprimorar sua abordagem de trading, esses algoritmos oferecem soluções valiosas que podem elevar o desempenho das suas operações nos mercados competitivos de hoje.

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

    Últimos Comentários | Ir para discussão (6)
    i_vergo
    i_vergo | 16 mai. 2025 em 07:44
    Ó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.
    i_vergo
    i_vergo | 16 mai. 2025 em 07:57
    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] | 16 mai. 2025 em 07:57
    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.

    CapeCoddah
    CapeCoddah | 16 mai. 2025 em 12:01

    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

    Shashin Wijewardhane
    Shashin Wijewardhane | 11 jul. 2025 em 08:40
    //+------------------------------------------------------------------+
    //| 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;
       }
    }
    //+------------------------------------------------------------------+
    Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
    Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
    Otimização e ajuste fino do código-fonte para melhorar os resultados do backtesting Otimização e ajuste fino do código-fonte para melhorar os resultados do backtesting
    Melhore seu código MQL5 otimizando a lógica, aprimorando os cálculos e reduzindo o tempo de execução para aumentar a precisão do backtesting. Ajuste finamente os parâmetros, otimize loops e elimine ineficiências para melhorar o resultado.
    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.
    Rede neural na prática: Uma questão de escala Rede neural na prática: Uma questão de escala
    Existe uma falsa sensação por parte do grande público, de que uma rede neural, ou inteligência artificial, consegue de alguma forma compreender o mundo em que vivemos. Isto em alguns casos pode até ser verdade. Mas você, que deseja fazer com que uma rede neural, ou mesmo um neurônio, possa conseguir convergir mais rápido. Precisa entender que a escala usada nos valores, em muitas das vezes acaba influenciando a velocidade de convergência. Além disto, neste artigo, irei mostrar algo bizarro que acontece em redes neurais. Algo que vai te levar a loucura, e a se questionar sobre o tema.