preview
Advanced Order Execution Algorithms in MQL5: TWAP, VWAP, and Iceberg Orders

Advanced Order Execution Algorithms in MQL5: TWAP, VWAP, and Iceberg Orders

MetaTrader 5Trading systems | 15 May 2025, 07:07
462 0
N Soumik
N Soumik
  1. Introduction
  2. Understanding Execution Algorithms
  3. Implementation in MQL5
  4. Performance Analyzer Implementation
  5. Comparing Algorithm Performance
  6. Integrating Execution Algorithms with Trading Strategies
  7. Integration Examples
  8. Integrated Strategy
  9. Backtest Results
  10. Conclusion



Introduction

Imagine you’re standing at the trading floor’s edge, heart pounding as prices tick by in real time. One wrong move, one oversized order, and your edge evaporates in a flash. Welcome to the world where execution quality isn’t just a nice-to-have—it’s the secret weapon separating winners from the rest.

For decades, institutional heavyweights have quietly wielded sophisticated algorithms to slice, dice, and stealthily deploy their orders, all to dodge slippage and tame market impact. Now, thanks to the flexibility of MQL5, that same powerhouse playbook is within reach of every ambitious retail trader.

What’s the Big Deal?

Picture this: you spot a golden opportunity and decide to go big. You slam in a market order for your full size, only to watch the price slip away under the weight of your own trade. In seconds, your ideal entry becomes a shaky compromise. That’s the notorious drag of market impact—and it bites even in the most liquid venues.

Execution algorithms are your antidote. By breaking a large order into a sequence of smaller, strategically timed slices, they smooth out your footprint on the order book. The result? Less slippage, tighter fills, and an overall improvement in your average execution price.

From Ivory Towers to Your Desktop

“Sure,” you might shrug, “but I’m not moving institutional sums.” Here’s the kicker: you don’t have to. Whether you’re deploying a half-lot or a handful of mini-lots, volatility can still twist your execution. These tools help you:

  • Tame Slippage: Even modest orders can wander in choppy markets.
  • Sharpen Your Edge: Layered executions often land you a more favorable average price than a one-shot gamble.
  • Stay Zen: Automated workflows strip away the temptation to panic-buy or panic-sell.
  • Scale Seamlessly: As your account grows, your execution stays crisp—no matter how hefty your orders become.
  • Fly Under the Radar: Iceberg Orders, in particular, cloak your true order size, keeping prying algos guessing.

Today’s democratized landscape means the same execution tech that once demanded multi-million-dollar budgets can now run on your personal trading station. By dropping polished MQL5 code for TWAP, VWAP, and Iceberg strategies into your platform, you’ll arm yourself with institutional firepower—without ever leaving the retail domain.

Get ready to flip the script on your execution process. The game is changing, and with these algorithms in your toolkit, you’ll be playing to win.


Understanding Execution Algorithms

Before diving into the implementation details, it's essential to understand the theory behind each execution algorithm and why they're effective in different market scenarios.

  1. Time-Weighted Average Price (TWAP): TWAP is a straightforward execution algorithm that divides a large order into equal parts and sends them at fixed time intervals over a set period. Its aim is to match the average price of the instrument over that time.
    • How it works:

      • Sends orders at regular time intervals between start and end.
      • Usually uses equal-sized orders (though you can add randomness to sizes).
      • Follows a predetermined timetable, regardless of price moves.
      • Spreads market impact evenly over time to keep slippage low.

    • When to use:

      • You need an average execution price over a specific timeframe.
      • Liquidity is steady throughout the trading period.
      • You have a fixed window to complete your order.
      • You prefer a simple, predictable approach.

  2. Volume-Weighted Average Price (VWAP): VWAP improves on TWAP by weighting order sizes according to expected volume. Instead of equal chunks, it sends larger trades when volume tends to be higher.
    • How it works:

      • Allocates order size in proportion to historical volume patterns.
      • Analyzes past trading volumes to predict future volume distribution.
      • Can adapt to real-time volume changes in some implementations.
      • Executes more during high-volume periods to reduce impact.

    • When to use:

      • Your performance is measured against VWAP.
      • Volume follows a predictable daily pattern.
      • You are trading in a market where liquidity varies through the session.
      • You want to align with the market’s natural flow.

  3. Iceberg Orders: Iceberg Orders focus on hiding the true size of a large order. Only a small “tip” is visible at any time; once it fills, the next portion appears.
    • How it works:

      • Displays only part of the total order.
      • Releases new visible chunks after each tip is executed.
      • You can fix or randomize the visible size to reduce detection.
      • Often placed as limit orders for better price control.

    • When to use:

      • You need to conceal your full order size.
      • The market is not very liquid and large trades can move prices.
      • You want to maintain execution at a specific price level.
      • You’re concerned about other traders detecting and front-running your order.


Implementation in MQL5

    Now that we understand the theory behind these execution algorithms, let's implement them in MQL5. We'll create a modular, object-oriented framework that allows these algorithms to be used individually or combined into a unified execution system.

    Base Class: CExecutionAlgorithm

    We'll start by defining a base class that provides common functionality for all execution algorithms:

    //+------------------------------------------------------------------+
    //| 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; }
    };
    

    The PlaceOrder method is particularly important as it handles the actual order execution and updates the volume tracking:

    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;
    }
    

    This function builds and sends a market order by zero‐initializing an MqlTradeRequest and MqlTradeResult, filling in symbol, volume, order type, price, slippage and a magic number, then calling OrderSend. If the send fails or the broker’s return code isn’t TRADE_RETCODE_DONE, it logs the error and returns false. On success it updates internal counters (total/fill counts, executed and remaining volume), recalculates the average price, captures the ticket ID, and returns true.

    Implementation of TWAP

    The TWAP algorithm divides the execution period into equal time intervals and places orders of equal (or randomized) size at each interval:

    //+------------------------------------------------------------------+
    //| 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();
    };
    

    Key Method: CalculateNextExecutionTime

    This method ensures orders are properly spaced over time, with an initial delay for the first order:
    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;
    }
    

    This method splits the window from m_startTime to m_endTime into m_intervals equal segments and returns when the next trade should fire: on the very first call it’s simply m_startTime + m_initialDelay, and on every subsequent call it’s TimeCurrent() + one interval’s worth of seconds (but never past m_endTime).

    Execute Method:The Execute method checks if it's time to place an order and handles the actual order placement:

    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;
    }
    

    This Execute method manages one slice of your TWAP run. First it aborts if the strategy isn’t active or if it isn’t yet time to trade. When it is, it picks either a fixed or randomized slice of your remaining volume (never exceeding what’s left), then looks up the current ask (for buys) or bid (for sells). It logs the interval, volume and price, builds an MqlTradeRequest with your symbol, volume, type, price, slippage and magic number, and calls OrderSend. If the send fails or the broker returns anything other than TRADE_RETCODE_DONE, it prints an error and returns false.

    On success it increments your order counters, adjusts executed and remaining volume, bumps the interval count, marks that the first order went out, then either schedules the next execution time or deactivates the strategy if you’ve run out of intervals or volume. Finally it logs what happened and returns true.

    Implementation of VWAP

    The VWAP algorithm is similar to TWAP but distributes order sizes based on historical volume patterns:
    //+------------------------------------------------------------------+
    //| 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();
    };
    
    Like TWAP, VWAP also implements the CalculateNextExecutionTime method to ensure proper spacing of orders:
    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;
    }
    

    Implementation of Iceberg Orders

    Iceberg Orders hide the true size of an order by exposing only a small portion to the market at any given time:

    //+------------------------------------------------------------------+
    //| 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; }
    };
    
    The Execute method places a new visible portion of the order:
    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;
    }
    

    When Execute runs, it first verifies the algorithm is active. If there’s already a live iceberg child order, it calls CheckAndReplaceOrder() to see if it needs cancellation or refill. Otherwise, it picks a visible slice (either fixed or randomized), caps it by the remaining total, and logs size and price.

    It then builds a pending‐order request ( TRADE_ACTION_PENDING ) with symbol, volume, limit price, slippage, and magic number, and calls OrderSend. On error or non-done return codes it logs and returns false; on success it saves the new ticket, marks the order active, records placement time, logs the details, and returns true.

    The Update method includes order timeout detection to ensure orders don't remain active indefinitely:

    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 periodically polls market and order status at intervals defined by m_checkInterval. If all volume is done, it terminates. Otherwise, once the check time arrives, it logs current bid/ask and limit price. If an order is active, it tests for timeout: if expired, it cancels, resets state, sleeps for m_orderPlacementDelay, and re-executes to place a fresh slice; if not timed out, it defers to CheckAndReplaceOrder(). If there’s no active order, it simply calls Execute to send the next visible portion.


    Performance Analyzer Implementation

    Our performance analyzer class tracks these metrics and provides methods to analyze and compare algorithm performance:
    //+------------------------------------------------------------------+
    //| 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; }
    };
    

    The CPerformanceAnalyzer encapsulates all of your post‐trade metrics in one place. When you construct it, you give it a symbol and the decision‐time benchmark price; it stamps the current time as the start. As each child order fills, you call RecordExecution(time, price, volume), which updates running totals—cumulative volume, weighted average execution price, and timestamps. Once the strategy finishes (or periodically), you call CalculateMetrics(), which computes:

    • Implementation shortfall (the P&L difference between your decision price and the actual executions),
    • Average slippage versus quoted prices,
    • Estimated market impact from your footprint,
    • Total execution time (end minus start),
    • Price improvement if any versus benchmarks.

    You can even compare two runs via CompareAlgorithms(otherAnalyzer) to see which strategy fared better. Finally, PrintReport() spits the key stats to the log for quick review, and SaveReportToFile(filename) lets you persist a full report externally. Lightweight getters expose each metric for custom dashboards or further analysis.


    Comparing Algorithm Performance

    Different market conditions favor different execution algorithms. Here's a general comparison i.e. rule of thumb:
    1. TWAP:
      • Best for: Stable markets with consistent liquidity
      • Advantages: Simple, predictable execution pattern
      • Disadvantages: Doesn't adapt to changing market conditions
    2. VWAP:
      • Best for: Markets with predictable volume patterns
      • Advantages: Aligns with natural market rhythm, often achieves better prices
      • Disadvantages: Requires historical volume data, more complex implementation
    3. Iceberg Orders:
      • Best for: Less liquid markets or when price sensitivity is high
      • Advantages: Minimizes market impact, maintains price control
      • Disadvantages: Execution time can be unpredictable, risk of partial execution


    Integrating Execution Algorithms with Trading Strategies

    The true power of these execution algorithms emerges when they're integrated with trading strategies. This section demonstrates how to incorporate our execution algorithms into complete trading systems.

    Execution Manager

    To simplify integration, we'll create an Execution Manager class that serves as a facade for all our execution algorithms:

    //+------------------------------------------------------------------+
    //| 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; }
    };
    

    The CExecutionManager acts as a simple façade over any of your execution algorithms and ties them into a unified trading workflow. Internally it holds a pointer to the currently selected CExecutionAlgorithm (TWAP, VWAP or Iceberg) plus a CPerformanceAnalyzer to track how well your orders are doing.

    You pick your strategy by calling one of the Create… methods—passing in symbol, total volume, start/end times (for TWAP/VWAP), interval counts or slice sizes, order type and any algorithm-specific knobs (randomization, history window, limit price, etc.). Once created, you drive it through the usual lifecycle:

    1. Initialize() sets up any state or data you need.
    2. Execute() fires off the next slice or child order.
    3. Update() polls fills, market data or timeouts.
    4. Terminate() cleans up once you’ve filled everything or want to stop.

    If you enable performance analysis with EnablePerformanceAnalysis(), the manager will record execution prices against a decision benchmark, and you can dump a concise P/L and slippage report via PrintPerformanceReport(). You can always grab the raw algorithm object with GetAlgorithm() for custom probing or metrics.


    Integration Examples

    Here are examples of how to integrate our execution algorithms with different types of trading strategies:

    1. Trend-Following Strategy with TWAP Execution.

      //+------------------------------------------------------------------+
      //| 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. Mean-Reversion Strategy with VWAP Execution.

      //+------------------------------------------------------------------+
      //| 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. Breakout Strategy with Iceberg Orders.
      //+------------------------------------------------------------------+
      //| 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.");
               }
            }
         }
      }
      

    Across all three examples, the integration follows the same high-level pattern:

    1. Maintain state

      • A boolean inPosition tracks whether you’re currently filling an order.
      • A static CExecutionManager executionManager lives across ticks to manage your chosen algorithm’s lifecycle.

    2. Entry logic

      • On your entry signal (MA crossover, RSI threshold, channel break), call the appropriate creation method on executionManager (TWAP, VWAP or Iceberg), passing symbol, total volume, time window or limit price, slice parameters, and order type.
      • If creation succeeds, immediately call executionManager.Initialize(), set inPosition=true, and log your start.

    3. Ongoing execution

      • While inPosition is true, every OnTick() invoke executionManager.Update().
      • Inside Update(), the manager will internally call Execute() as needed, poll fills, handle timeouts or market updates, and schedule the next slice (or cancel/replace child orders for Iceberg).

    4. Completion & exit

      • After each update, check executionManager.GetAlgorithm()->IsActive(). Once it returns false (all intervals done or volume exhausted), set inPosition=false and log that execution has completed.
      • In the VWAP mean-reversion example, there’s an extra exit check: if price reverses beyond your RSI threshold mid-execution, you call executionManager.Terminate() to stop early.

    Rules for each are as follows:
    1. Trend-Following + TWAP
      Entry: Fast MA crossing above slow MA triggers
      Execution: Splits your 1 lot buy into 5 equal slices over the next hour

    2. Mean-Reversion + VWAP
      Entry: RSI < 30 for buys, > 70 for sells
      Execution: Distributes 1 lot against historical volume over 5 slices in 1 hour
      Early Exit: If the signal flips (e.g. RSI > 70 during a buy), executionManager.Terminate() aborts remaining slices

    3. Breakout + Iceberg
      Entry: Price breaks channel high (buy) or low (sell)
      Execution: Places a pending limit at the breakout price, revealing only ~0.1 lots at a time and refilling until the full 1 lot is done

    By swapping out CreateTWAP, CreateVWAP or CreateIcebergOrder, you plug any execution algorithm into your signal logic without duplicating order-management boilerplate.


    Integrated Strategy

    Full Code:

    //+------------------------------------------------------------------+
    //|                                  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);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    The IntegratedStrategy.mq5 expert advisor begins by declaring its metadata (copyright, link, version) and including the headers for all of our execution-algorithm classes and the performance analyzer. It then defines four user-adjustable inputs: the fast and slow SMA periods, the total trading volume per signal, and a boolean flag to toggle “adaptive” execution (where the manager decides whether to use TWAP, VWAP or Iceberg under the hood). A global pointer to CExecutionManager and two indicator handles are also declared so they persist between ticks.

    In OnInit(), we instantiate the execution manager—passing it the current symbol, a maximum of three concurrent algorithms, and our adaptive flag—and then create the two SMA indicator handles. If either handle fails, initialization aborts. OnDeinit() simply cleans up by deleting the manager and releasing the indicator handles, ensuring no memory or handle leaks when the EA is removed or the platform shuts down.

    The core logic lives in OnTick(). First, we call UpdateAlgorithms() on the execution manager so that any existing child orders (TWAP slices, VWAP buckets or Iceberg legs) get processed, cancelled or refilled as needed. Then we wait for a new bar (by skipping if tick volume is still building). Once at bar-open, we pull the last two SMA values for both fast and slow periods. A crossover from below to above triggers a buy signal; the reverse triggers a sell.

    If adaptive execution is enabled, we hand off the signal and volume to g_executionManager.ExecuteSignal(), letting it pick the appropriate algorithm. Otherwise, we manually spin up a TWAP instance for a one-hour window and six slices. This pattern cleanly separates your entry logic (trend detection) from your order-management logic, letting the same facade drive multiple execution styles without duplicating boilerplate.

    After incorporation Take Profit, the code changes to:

    //+------------------------------------------------------------------+
    //|                                  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);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    Backtest Results

    1. Equity & Balance Curves

    The green “Balance” stair-steps show your account’s book equity whenever the EA closes a position; the blue “Equity” line smooths in unrealized P&L between trades. We can see a clear upward trend from January through early March, with a few pullbacks—each drawdown topping out around 10–16% before your next series of wins restores the gain. That pattern suggests the system thrives in trending conditions but still suffers tolerable equity dips.

    2. Volume & Risk Utilization

    At the bottom, the “Deposit Load” triangles shrink gradually over time—this is your position size as a percentage of equity. It starts near 10% of your balance and tapers as your equity grows (with fixed-volume sizing), meaning ourrisk per trade actually decreases as the account climbs. That’s why drawdowns stay proportionally similar even as your dollar equity rises.

    3. Key Profitability Metrics

    • Initial deposit: $1,000

    • Net profit: + $703 (a 70% return over ~2 months)

    • Profit factor: 2.34 (you make $2.34 for every $1 lost)

    • Expected payoff: $2.34 per trade on average

    • Sharpe ratio: 5.47 (very high—strong risk-adjusted returns)

    These figures tell us the strategy is not only profitable but earns a healthy buffer above its own volatility.

    4. Drawdown & Recovery

    • Max balance drawdown: 156 points or 9.99%

    • Max equity drawdown: 228 points or 15.89%

    • Recovery factor: 3.08 (net profit ÷ max drawdown)

    A recovery factor above 2 is generally considered good, so at 3.08 you’re generating over three times your worst loss in gain.

    5. Trade Distribution

    • Total trades: 300 (600 deals, so every entry+exit counts as two)

    • Win rate: 76% (228 winners vs. 72 losers)

    • Average win: $5.39

    • Average loss: – $7.31

    Although your win rate and profit factor are strong, notice that your losers are on average larger than winners—something to watch if market conditions flip.

    6. Streaks & Consistency

    • Max consecutive wins: 87 trades, + $302

    • Max consecutive losses: 23 trades, – $156

    • Average winning streak: 57 trades

    • Average losing streak: 18 trades

    Long winning streaks drive the upward slope, while the longest losing run still only costs about 15% of equity.


    Conclusion

    Imagine you’re a solo trader in a crowded market arena—every tick matters, every fill price whispers profit or loss. By weaving TWAP, VWAP and Iceberg Orders into your toolkit, you’re no longer just reacting to price swings; you’re orchestrating them. These once-elite, institutional-grade algorithms are now at your fingertips, slicing through liquidity like a laser and turning chaotic order books into opportunities.

    TWAP becomes your steady metronome, pacing your size evenly across a set interval—perfect for when the tide is calm and you simply want a smooth ride. VWAP morphs you into a savvy volume-tracker, attacking the heaviest trading beats of the day and riding the market’s own pulse. And when you need to cloak your intentions, Iceberg Orders slip your true size beneath the surface, revealing just enough to get filled without spooking the big players.

    But these aren’t just standalone tricks. With our modular MQL5 framework, you plug them into any strategy—trend followers, mean-reverters, breakout hunters—with the ease of snapping on a new lens. A single ExecutionManager façade lets you swap, combine or even layer algorithms mid-trade, while the PerformanceAnalyzer keeps score like a hawk, measuring slippage, shortfall and market impact down to the last pip.

    What’s next? Think of execution as a living creature that adapts. Let your TWAP learn from volatility spikes. Route your VWAP slices to the deepest pools. Teach your Iceberg to sense where predators lurk and hide deeper. And why stop there? Inject machine-learning to predict the perfect microsecond to fire, or blend order types into bespoke hybrids that match your unique edge.

    The trading world never stands still—and neither should your order execution. Dive in, experiment boldly, and turn every slice, every fill, into a calculated advantage. Your edge awaits in the code.

    For your convenience, here's a summary of the files included with this article:

    File Name Description
    ExecutionAlgorithm.mqh Base class for all execution algorithms 
    TWAP.mqh Time-Weighted Average Price implementation
    VWAP.mqh Volume-Weighted Average Price implementation
    IcebergOrder.mqh Iceberg Order implementation
    PerformanceAnalyzer.mqh Tools for analyzing execution performance
    ExecutionManager.mqh Facade for easy integration with trading strategies
    IntegratedStrategy.mq5
    Example EA showing integration with a trading strategy
    IntegratedStrategy - Take Profit.mq5 Example EA showing integration with a trading strategy with take profit and stop loss in percentage on the account balance
    By incorporating these advanced execution algorithms into your trading toolkit, you're taking a significant step toward more professional and efficient trading. Whether you're looking to minimize the impact of larger trades, improve your average execution prices, or simply add more sophistication to your trading approach, these algorithms provide valuable solutions that can enhance your trading performance in today's competitive markets.
    Features of Custom Indicators Creation Features of Custom Indicators Creation
    Creation of Custom Indicators in the MetaTrader trading system has a number of features.
    From Basic to Intermediate: Arrays and Strings (III) From Basic to Intermediate: Arrays and Strings (III)
    This article considers two aspects. First, how the standard library can convert binary values to other representations such as octal, decimal, and hexadecimal. Second, we will talk about how we can determine the width of our password based on the secret phrase, using the knowledge we have already acquired.
    Features of Experts Advisors Features of Experts Advisors
    Creation of expert advisors in the MetaTrader trading system has a number of features.
    Developing a Replay System (Part 68): Getting the Time Right (I) Developing a Replay System (Part 68): Getting the Time Right (I)
    Today we will continue working on getting the mouse pointer to tell us how much time is left on a bar during periods of low liquidity. Although at first glance it seems simple, in reality this task is much more difficult. This involves some obstacles that we will have to overcome. Therefore, it is important that you have a good understanding of the material in this first part of this subseries in order to understand the following parts.