MQL4: How to Close All Open Orders?

Automated trading systems built with MQL4 require robust order management capabilities. One common requirement is the ability to close all open orders under specific conditions. This article delves into the process of achieving this effectively in MQL4, targeting developers with existing knowledge of the platform.

Introduction to Closing All Open Orders in MQL4

Trading automation relies heavily on precise control over trading positions. In the dynamic Forex market, strategic decisions often necessitate the simultaneous closure of multiple trades to manage risk, secure profits, or exit a losing scenario.

Understanding the Importance of Order Management in MQL4

Effective order management is the cornerstone of any reliable Expert Advisor (EA) or script. It involves not just opening and modifying trades, but crucially, closing them accurately and promptly. Poor order management can lead to unexpected exposure, missed profit opportunities, or excessive losses.

Brief Overview of MQL4 and MetaTrader 4

MQL4 is the programming language used within the MetaTrader 4 platform for developing trading robots, custom indicators, and scripts. It provides a set of functions to interact with the trading environment, access market data, and manage trading operations, including opening, modifying, and closing orders.

Why Close All Orders? Use Cases and Scenarios

The need to close all open orders arises in various situations:

  • Strategy Exits: A trading strategy might dictate exiting all current positions when a major support/resistance level is broken, a specific time of day is reached, or a counter-signal appears.
  • Risk Management: Implementing a global stop-loss across all trades, or exiting all positions during high-impact news events.
  • Script Utilities: Creating a simple script to quickly clear the terminal of all open trades for testing or manual intervention.
  • Account Management: Closing all positions before switching accounts or winding down trading activity.

Core Function: OrderClose() in MQL4

The primary function for closing an individual order in MQL4 is OrderClose(). Understanding its parameters and behavior is fundamental to closing multiple orders.

Syntax and Parameters of the OrderClose() Function

The OrderClose() function attempts to close a previously selected order. Its signature is:

bool OrderClose(
   int ticket,      // order ticket
   double lots,     // volume
   double price,    // close price
   int slippage,   // slippage
   color arrow_color // color
);
  • ticket: The unique identifier (ticket number) of the order to be closed. This is obtained via OrderSelect().
  • lots: The volume (in lots) to close. To close the entire order, use OrderLots().
  • price: The desired closing price. For market orders, this is typically Bid for OP_SELL or Ask for OP_BUY. However, the actual closing price might differ due to slippage.
  • slippage: The maximum allowable difference in pips between the requested price and the actual execution price. If the price movement exceeds this value, the order will not be closed.
  • arrow_color: Optional parameter to draw a small arrow on the chart at the close price level. Use CLR_NONE or omit for no arrow.

Return Values and Error Handling

OrderClose() returns true on success and false on failure. Failures can occur due to various reasons, such as:

  • Invalid ticket number.
  • Invalid parameters (e.g., volume).
  • Broker-side rejection.
  • Market conditions (e.g., requotes, market busy).

Upon failure, the specific error code can be retrieved using GetLastError(). Handling these errors is crucial for robust order management.

Practical Examples of Using OrderClose()

Closing a specific order with a known ticket number:

int myTicket = 12345;
double closePrice = Ask; // Assuming it's a buy order
double volume = OrderLots(); // Close full volume
int maxSlippage = 5;

// Select the order first
if(OrderSelect(myTicket, SELECT_BY_TICKET))
{
   // Check if it's an open position (not pending)
   if(OrderType() == OP_BUY || OrderType() == OP_SELL)
   {
      // Determine closing price based on order type
      double price = (OrderType() == OP_BUY) ? Bid : Ask;

      if(OrderClose(myTicket, volume, price, maxSlippage, CLR_NONE))
      {
         Print("Order ", myTicket, " closed successfully.");
      }
      else
      {
         Print("Error closing order ", myTicket, ". Error: ", GetLastError());
      }
   }
}
else
{
   Print("Failed to select order ", myTicket, ". Error: ", GetLastError());
}

Note: Always select the order using OrderSelect() before attempting to close it, even if you have the ticket number, to populate its properties.

Implementing a Function to Close All Open Orders

The core logic for closing all open orders involves iterating through the list of orders available in the terminal and applying the OrderClose() function to each open position.

Iterating Through Open Orders with OrderSelect()

MQL4 provides OrdersTotal() to get the total number of current orders (including pending and closed historical orders if selected appropriately, but for open orders, it’s simpler). The OrderSelect(index, SELECT_BY_POS) function is used to select an order by its index in the terminal’s order pool.

It is critical to iterate through the orders in reverse order (from OrdersTotal() - 1 down to 0). This is because closing an order changes the total number of orders and re-indexes the list. Iterating forwards would cause you to skip orders.

for (int i = OrdersTotal() - 1; i >= 0; i--)
{
   // Select order by index
   if (OrderSelect(i, SELECT_BY_POS))
   {
      // Process the selected order
      // ... check type, symbol, etc. ...
   }
}

Identifying Order Types (OPBUY, OPSELL) and Magic Numbers

Inside the loop, after successfully selecting an order, you need to check if it’s an open position (OP_BUY or OP_SELL) and potentially if it belongs to your EA using its magic number (OrderMagicNumber()).

if (OrderSelect(i, SELECT_BY_POS))
{
   // Check if it's an open Buy or Sell position
   if (OrderType() == OP_BUY || OrderType() == OP_SELL)
   {
      // Optional: Check magic number
      if (OrderMagicNumber() == YourEAMagicNumber)
      {
         // This is an open order managed by this EA
         // ... prepare to close ...
      }
      // Optional: To close ALL open orders regardless of magic number, remove the magic number check.
   }
}

Creating a Custom Function: CloseAllOrders()

It’s best practice to encapsulate this logic within a dedicated function. This makes your code modular and reusable.

void CloseAllOrders(
   int magicNumber = 0, // 0 to close all, > 0 to close by magic number
   string symbol = NULL // NULL to close all symbols, specify symbol otherwise
)
{
   // Iteration logic goes here
}

Code Snippet: Complete Function Implementation

Here is a complete function that iterates through orders and closes all open positions, with optional filtering by magic number and symbol.

void CloseAllOrders(
   int magicNumber = 0,   // 0 to close all, > 0 to close by magic number
   string symbol = NULL    // NULL to close all symbols, specify symbol otherwise
)
{
   for (int i = OrdersTotal() - 1; i >= 0; i--)
   {
      // Select order by index
      if (OrderSelect(i, SELECT_BY_POS))
      {
         int orderType = OrderType();

         // Check if it's an open Buy or Sell position
         if (orderType == OP_BUY || orderType == OP_SELL)
         {
            // Apply magic number filter if specified
            bool magicMatch = (magicNumber == 0 || OrderMagicNumber() == magicNumber);

            // Apply symbol filter if specified
            bool symbolMatch = (symbol == NULL || OrderSymbol() == symbol);

            if (magicMatch && symbolMatch)
            {
               int ticket = OrderTicket();
               double volume = OrderLots();
               double price = (orderType == OP_BUY) ? Bid : Ask; // Use Bid for Buy, Ask for Sell
               int slippage = 10; // Define your acceptable slippage

               // Attempt to close the order
               if (OrderClose(ticket, volume, price, slippage, CLR_NONE))
               {
                  Print("Closed Order: ", ticket, " Type: ", orderType, " Lots: ", volume, " Price: ", price);
               }
               else
               {
                  // Handle closure failure
                  int error = GetLastError();
                  Print("Failed to close Order: ", ticket, " Error: ", error);
                  // Consider retry logic or further error analysis here
               }
            }
         }
      }
   }
}

// Example usage in an EA or script:
// Close all open orders from this EA (assuming YourMagicNumber is defined)
// CloseAllOrders(YourMagicNumber);

// Example usage: Close all open orders on EURUSD regardless of magic number
// CloseAllOrders(0, "EURUSD");

// Example usage: Close ALL open orders on the account
// CloseAllOrders();

Adjust the slippage value based on the currency pair’s volatility and your broker’s execution characteristics.

Advanced Techniques and Considerations

Implementing the basic closure logic is a good start, but professional EAs require more robust handling.

Closing Orders Based on Symbol or Magic Number

The provided CloseAllOrders function already includes parameters for filtering by magicNumber and symbol. This is essential for EAs that trade multiple pairs or when you only want to manage positions opened by a specific instance of an EA.

Passing 0 for magicNumber closes orders regardless of their magic number (including manually opened ones if no symbol filter is applied). Passing NULL for symbol closes orders on any symbol.

Handling Errors and Retries During Order Closing

Market conditions can lead to OrderClose() failures (e.g., error 135 – ERRPRICECHANGED, 136 – ERRTOOMANY_REQUESTS). A simple retry mechanism can improve robustness. You could loop the OrderClose attempt a few times with a short delay, checking GetLastError() each time.

// Inside the CloseAllOrders loop after a failure:
int error = GetLastError();
Print("Failed to close Order: ", ticket, " Error: ", error);

// Simple retry logic (consider adding a delay in a real EA)
if (error == 135 || error == 136)
{
   Print("Attempting retry for order ", ticket);
   // Implement retry loop here with Sleep()
   // Example: bool closed = false;
   // for(int retry = 0; retry < 3; retry++) {
   //    Sleep(100);
   //    price = (orderType == OP_BUY) ? Bid : Ask; // Get fresh price
   //    if(OrderClose(ticket, volume, price, slippage, CLR_NONE)) { closed = true; break; }
   //    else { error = GetLastError(); if(error != 135 && error != 136) break; } // Break on non-retryable error
   // }
   // if(!closed) Print("Final failure for order ", ticket, ". Error: ", error);
}

Note: Retries should be implemented carefully, possibly outside the main order iteration loop to avoid complicating it and to allow for small delays using Sleep(), which is generally discouraged in event handlers like OnTick but acceptable in utility scripts or dedicated closure functions called at specific times.

Using OrderSend() to Close Opposite Positions (Hedging)

While OrderClose() is for exiting individual orders, in MetaTrader 4’s hedging mode (if the broker supports it, which is less common now for new accounts compared to netting), closing an opposite position of the same volume effectively neutralizes the exposure. This is not closing an order using OrderClose(). Instead, you would open a new order using OrderSend() with the opposite type and the volume you wish to ‘close’. For instance, to ‘close’ 1 lot of a BUY position, you would open a 1 lot SELL position. This approach is specific to hedging accounts and less relevant to the core topic of closing orders using OrderClose(). It’s a method of hedging exposure, not using the order management functions to exit trades.

Best Practices and Optimization

Writing efficient and reliable MQL4 code for order management is key.

Ensuring Correct Order Selection and Modification

Always select the order using OrderSelect() immediately before attempting any operation (OrderClose(), OrderModify(), OrderDelete()). Use SELECT_BY_POS in a reverse loop for iteration. After selecting, re-check the order’s properties (OrderTicket(), OrderType(), OrderMagicNumber()) to ensure you are acting on the intended order, as the order pool can change between loop iterations if other processes are also trading.

Testing and Debugging the CloseAllOrders() Function

Thorough testing is non-negotiable. Test your CloseAllOrders function extensively on a demo account under various market conditions. Use Print() statements to log which orders are being selected, attempted to be closed, and the outcome (success or error). The Strategy Tester in MT4 can also be used, though simulating order closing success/failure precisely can be tricky.

Integrating the Function into an EA or Script

In an Expert Advisor, you might call CloseAllOrders() from OnTick(), OnTimer(), or based on specific trading signals. A common use case is calling it from OnDeinit() when the EA is removed from the chart, ensuring a clean exit from all positions managed by that EA.

For a utility script, the function would typically be called directly within the script’s main body (start() function in MQL4 scripts), executing once and terminating.

Conclusion and Further Resources

Mastering the closure of orders is a vital skill for MQL4 developers. The OrderClose() function, combined with careful iteration using OrderSelect() in reverse order, forms the basis for closing individual or multiple positions. Implementing this logic within a reusable function like CloseAllOrders enhances code structure. Remember to account for potential errors, test rigorously, and consider filtering by magic number or symbol for precision.

For more detailed information on OrderClose(), OrderSelect(), and error codes, consult the official MQL4 Reference documentation within the MetaEditor help section (press F1). Understanding transaction properties and potential execution delays is key to developing robust automated trading systems.


Leave a Reply