
Advanced Order Execution Algorithms in MQL5: TWAP, VWAP, and Iceberg Orders
- Introduction
- Understanding Execution Algorithms
- Implementation in MQL5
- Performance Analyzer Implementation
- Comparing Algorithm Performance
- Integrating Execution Algorithms with Trading Strategies
- Integration Examples
- Integrated Strategy
- Backtest Results
- 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.
- 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.
-
- 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.
-
- 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:- TWAP:
- Best for: Stable markets with consistent liquidity
- Advantages: Simple, predictable execution pattern
- Disadvantages: Doesn't adapt to changing market conditions
- 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
- 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:
- Initialize() sets up any state or data you need.
- Execute() fires off the next slice or child order.
- Update() polls fills, market data or timeouts.
-
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:
-
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."); } } } }
-
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."); } } }
- 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:
-
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.
-
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.
-
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).
-
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.
- 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 - 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 - 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 |





- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use