Discussing the article: "Advanced Order Execution Algorithms in MQL5: TWAP, VWAP, and Iceberg Orders"

 

Check out the new article: Advanced Order Execution Algorithms in MQL5: TWAP, VWAP, and Iceberg Orders.

An MQL5 framework that brings institutional-grade execution algorithms (TWAP, VWAP, Iceberg) to retail traders through a unified execution manager and performance analyzer for smoother, more precise order slicing and analytics.

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

Author: N Soumik

 

Great article!

Which pairs do you recommend for this Algo?

Which time frames? M5, M30 etc.

Which session?

Thanks and kind regards

 
Great Article
Testing your algo.
IN file, ExecutionAlgorithm.mqh, added this line    request.type_filling = ORDER_FILLING_IOC; in placing order to fix order placing issue.
Back tested it at M5, it opened only 1 trade for 2 months period, no partial order opened.
Tested it at H1, it never applied the SL, or TP and all trades closed in loss.

also while compiling it generates warnings
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 271 41
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 272 22
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 449 17
possible loss of data due to type conversion from 'long' to 'double' PerformanceAnalyzer.mqh 222 17
possible loss of data due to type conversion from 'long' to 'double' ExecutionManager.mqh 418 17


suggest how tyo test the algo, 
Time frame. and any other recommendations.
 
to fix the warnins
also while compiling it generates warnings
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 271 41
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 272 22
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 449 17
possible loss of data due to type conversion from 'long' to 'double' PerformanceAnalyzer.mqh 222 17
possible loss of data due to type conversion from 'long' to 'double' ExecutionManager.mqh 418 17


i changed the code line
m_volumeProfile[intervalIndex] += rates[i].tick_volu

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

It fixed the warnings
Now need you guidence regarding my other queries, as 
Time frame
And also 
why all trades during backtest result in Loss
how to test this great work from you..
 
i_vergo #:
Great Article
Testing your algo.
IN file, ExecutionAlgorithm.mqh, added this line    request.type_filling = ORDER_FILLING_IOC; in placing order to fix order placing issue.
Back tested it at M5, it opened only 1 trade for 2 months period, no partial order opened.
Tested it at H1, it never applied the SL, or TP and all trades closed in loss.

also while compiling it generates warnings
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 271 41
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 272 22
possible loss of data due to type conversion from 'long' to 'double' VWAP.mqh 449 17
possible loss of data due to type conversion from 'long' to 'double' PerformanceAnalyzer.mqh 222 17
possible loss of data due to type conversion from 'long' to 'double' ExecutionManager.mqh 418 17


suggest how tyo test the algo, 
Time frame. and any other recommendations.

The warnings aren't the problem but could get fixed quickly. But yes it would be great if the author could show step-by-step which settings and inputs he used for the backtest.

 

I agree with Dominic as the warnings are just warnings.  I_Virgo's results are probably because he used the wrong time frame and currency pair.  From the Back Test report, of nearly 2000 bars, it must have been either M1 or M5 as the time frame with an unknown pair.

It would be nice if MQ added the time frame and currency pair or pairs  and also separated the pair results in more details to the Back Test report so we could more closely replicate author's back test results as well as determining its applicability over the forex pairs..  Also, it would be extremely helpful if the EA could post text to the chart while it is running.


I also think it is a great article and plan to study it thoroughly in anticipation of adapting his techniques to other EAs


CapeCoddah

 
//+------------------------------------------------------------------+
//| Base class for all execution algorithms                           |
//+------------------------------------------------------------------+
class CExecutionAlgorithm
{
protected:
   string            m_symbol;           // Trading symbol
   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 of execution
   datetime          m_endTime;          // End time of execution
   int               m_slippage;         // Allowed slippage in points
   bool              m_isActive;         // Is algorithm currently active
   
   // Statistics
   double            m_avgExecutionPrice; // Average execution price
   int               m_totalOrders;       // Total number of orders placed
   int               m_filledOrders;      // Number of filled orders
   
public:
   // Constructor
   CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
   
   // Destructor
   virtual ~CExecutionAlgorithm();
   
   // Virtual methods to be implemented by derived classes
   virtual bool      Initialize();
   virtual bool      Execute() = 0;
   virtual bool      Update() = 0;
   virtual bool      Terminate() = 0;
   
   // Common methods
   bool              IsActive() { return m_isActive; }
   double            GetExecutedVolume() { return m_executedVolume; }
   double            GetRemainingVolume() { return m_remainingVolume; }
   double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
   
   // Helper methods
   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);
   
   // Helper method to get appropriate filling mode
   ENUM_ORDER_TYPE_FILLING GetFillingMode();
};

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

//+------------------------------------------------------------------+
//| Destructor                                                        |
//+------------------------------------------------------------------+
CExecutionAlgorithm::~CExecutionAlgorithm()
{
   // Clean up resources if needed
}

//+------------------------------------------------------------------+
//| Initialize the algorithm                                          |
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::Initialize()
{
   // Validate inputs
   if(m_symbol == "" || m_totalVolume <= 0.0)
   {
      Print("Invalid inputs for execution algorithm");
      return false;
   }
   
   // Check if the symbol exists
   if(!SymbolSelect(m_symbol, true))
   {
      Print("Symbol not found: ", m_symbol);
      return false;
   }
   
   // Reset statistics
   m_executedVolume = 0.0;
   m_remainingVolume = m_totalVolume;
   m_avgExecutionPrice = 0.0;
   m_totalOrders = 0;
   m_filledOrders = 0;
   
   return true;
}

//+------------------------------------------------------------------+
//| Get appropriate filling mode for the symbol                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
{
   // Get symbol filling modes
   int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
   
   // Check available filling modes in order of preference
   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;
}

//+------------------------------------------------------------------+
//| Place an order                                                    |
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
{
   // Validate inputs
   if(volume <= 0.0)
   {
      Print("Invalid order volume");
      return false;
   }
   
   // Prepare the request
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.symbol = m_symbol;
   request.volume = volume;
   request.type = orderType;
   request.deviation = m_slippage;
   request.magic = 123456; // Magic number for identification
   
   // Set appropriate action and price based on order type
   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
   {
      // Market order
      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
   {
      // Pending order
      request.action = TRADE_ACTION_PENDING;
      if(price <= 0.0)
      {
         Print("Price must be specified for pending orders");
         return false;
      }
      request.price = price;
   }
   
   // Send the order
   if(!OrderSend(request, result))
   {
      Print("OrderSend error: ", GetLastError());
      return false;
   }
   
   // Check the result
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   // Update statistics
   m_totalOrders++;
   
   // For market orders, update execution statistics immediately
   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;
}

//+------------------------------------------------------------------+
//| Modify an existing order                                          |
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
{
   // Prepare the request
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_MODIFY;
   request.order = ticket;
   request.price = price;
   request.sl = sl;
   request.tp = tp;
   
   // Send the modification request
   if(!OrderSend(request, result))
   {
      Print("OrderModify error: ", GetLastError());
      return false;
   }
   
   // Check the result
   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;
}

//+------------------------------------------------------------------+
//| Cancel an existing order                                          |
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::CancelOrder(ulong ticket)
{
   // Prepare the request
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_REMOVE;
   request.order = ticket;
   
   // Send the cancellation request
   if(!OrderSend(request, result))
   {
      Print("OrderCancel error: ", GetLastError());
      return false;
   }
   
   // Check the result
   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;
}

//+------------------------------------------------------------------+
//| Update the average execution price                                |
//+------------------------------------------------------------------+
void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
{
   // Calculate the new average execution price
   if(m_executedVolume > 0.0)
   {
      // Weighted average of old and new prices
      m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                           (m_executedVolume + volume);
   }
   else
   {
      // First execution
      m_avgExecutionPrice = price;
   }
}
//+------------------------------------------------------------------+