How to Close All Sell Orders in MQL4?

Introduction to Closing Sell Orders in MQL4

Automating trade management is a fundamental aspect of developing robust Expert Advisors (EAs) and scripts in MQL4. A common requirement is the ability to programmatically close specific types of orders based on certain conditions or events. This article focuses specifically on the process of identifying and closing all open sell orders within an MQL4 program.

Understanding Order Types in MQL4 (OP_SELL)

In MQL4, order types are represented by predefined constants. These constants identify the nature of a pending or open trade. For open positions, the relevant types are OP_BUY for long positions and OP_SELL for short positions. Pending orders include OP_BUYLIMIT, OP_SELLLIMIT, OP_BUYSTOP, and OP_SELLSTOP.

The constant OP_SELL specifically refers to an open short position, which was initiated by selling an instrument with the expectation that its price will fall.

Why Close All Sell Orders?

There are numerous scenarios in algorithmic trading where closing all sell orders might be necessary:

  • Risk Management: Implementing an emergency stop or closing all short positions during significant market events or volatile periods.
  • Strategy Execution: Reversing a strategy’s direction or exiting all current sell trades as part of a larger trading logic.
  • End-of-Day/Week Procedures: Closing positions before market close or weekend gaps.
  • Account Management: Freeing up margin or simplifying the order book.
  • Manual Intervention Scripts: Providing a user interface element (via a script) to quickly close all sell trades.

Regardless of the specific trigger, the underlying mechanism involves iterating through existing orders and selectively closing those that meet the criteria of being an open sell position.

Iterating Through Open Orders

To manage open orders in MQL4, you must access them one by one from the account’s order pool. This is achieved using a loop and the OrderSelect() function.

Using OrderSelect() Function

The OrderSelect() function is crucial for working with individual orders. It takes two arguments:

bool OrderSelect( int index, int select, int pool=MODE_TRADES );
  • index: The index of the order in the order pool (0 to OrdersTotal() – 1).
  • select: The selection method. SELECT_BY_POS is typically used to select by index, and SELECT_BY_TICKET by ticket number.
  • pool: The order pool to select from. MODE_TRADES selects from open and pending orders, while MODE_HISTORY selects from historical orders.

To iterate through all open and pending orders, you would loop from OrdersTotal() - 1 down to 0, calling OrderSelect(i, SELECT_BY_POS, MODE_TRADES) in each iteration.

for(int i = OrdersTotal() - 1; i >= 0; i--)
{
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
        // Access order properties here
    }
}

Iterating backward is a standard practice when modifying the order pool (like closing orders), as closing an order shifts the indices of subsequent orders.

Understanding Order Properties (OrderType(), OrderSymbol(), OrderMagicNumber())

Once an order is successfully selected using OrderSelect(), you can access its properties using dedicated MQL4 functions. For our purpose of closing sell orders, the most important properties are:

  • OrderType(): Returns the type of the selected order (e.g., OP_BUY, OP_SELL). This is essential for identifying sell orders.
  • OrderSymbol(): Returns the symbol of the selected order (e.g., “EURUSD”). This is useful if you only want to close sell orders on a specific symbol, not all symbols on the account.
  • OrderMagicNumber(): Returns the magic number assigned to the order. Magic numbers are crucial for EAs to identify their own orders and differentiate them from orders placed manually or by other EAs.

Filtering orders based on these properties allows for precise control over which orders are targeted for closure.

Implementing the Close All Sell Orders Function

Let’s structure the logic into a reusable function.

Creating a Custom Function: CloseAllSellOrders()

A dedicated function makes the code cleaner and easier to manage. We can name it CloseAllSellOrders.

void CloseAllSellOrders()
{
    // Loop through orders
    // Check order type
    // Close if it's a sell order
}

Filtering for Sell Orders (OrderType() == OP_SELL)

Inside the loop, after successfully selecting an order, we check its type:

for(int i = OrdersTotal() - 1; i >= 0; i--)
{
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
        if(OrderType() == OP_SELL)
        {
            // This is a sell order, now close it
        }
    }
}

You can add further checks here, such as OrderSymbol() == Symbol() to only affect orders on the current chart’s symbol, or OrderMagicNumber() == your_magic_number to only affect orders placed by this specific EA.

Closing Orders Using OrderClose()

The OrderClose() function is used to close a market order (buy or sell). Its signature is:

bool OrderClose( int ticket, double lots, double price, int slippage, color arrow_color=CLR_NONE);
  • ticket: The ticket number of the order to close (obtained via OrderTicket() after selection).
  • lots: The volume to close. For a full closure, this is OrderLots().
  • price: The closing price. For a sell order, this is the current Bid price (Bid).
  • slippage: The maximum allowed price slippage in points. Set to a reasonable value (e.g., 5-10 points).
  • arrow_color: Optional color for the closing arrow on the chart.

Putting it into the loop:

for(int i = OrdersTotal() - 1; i >= 0; i--)
{
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
        if(OrderType() == OP_SELL)
        {
            // Get necessary details before attempting close
            int ticket = OrderTicket();
            double lots = OrderLots();
            double close_price = Bid;
            int slippage_points = 5; // Define your acceptable slippage

            // Attempt to close the order
            if(OrderClose(ticket, lots, close_price, slippage_points))
            {
                Print("Sell Order ", ticket, " closed successfully.");
            }
            else
            {
                Print("Error closing sell order ", ticket, ". Error: ", GetLastError());
            }
        }
    }
}

Error Handling and Return Values

OrderClose() returns true on successful placement of the close request (though not necessarily immediate execution) and false if the request fails. It’s vital to check this return value and use GetLastError() if it’s false to understand the reason for the failure. Common errors include ERR_INVALID_PRICE, ERR_TRADE_TIMEOUT, ERR_NO_FX_ACCOUNT (less common now), ERR_TRADE_NOT_ALLOWED, etc.

Implementing basic error reporting using Print() is recommended during development and debugging.

Example Code and Usage

Complete MQL4 Code Snippet

Here is a complete example of the CloseAllSellOrders function that could be placed in an EA or script:

//+------------------------------------------------------------------+
//| Expert Advisor / Script                                          |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link      "Your Website"
#property version   "1.00"
#property strict

//--- input parameters
extern int      Slippage = 5; // Slippage in points
extern int      MagicNumber = 0; // Set to 0 to close orders with any magic number
extern bool     OnlyCurrentSymbol = true; // Close only orders on the current chart symbol

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Place any initialization code here
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Place any deinitialization code here
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//| This is just an example usage within OnTick                      |
//| In a real EA, you'd call CloseAllSellOrders based on strategy   |
//| conditions, not on every tick.                                   |
//+------------------------------------------------------------------+
void OnTick()
{
// Example: Call the function periodically or on a specific condition
// For demonstration, commented out to avoid closing on every tick.
// CloseAllSellOrders();
}

//+------------------------------------------------------------------+
//| Script start function                                            |
//| For a script, you might call this function directly              |
//+------------------------------------------------------------------+
void OnStart()
{
    // For a script, simply call the function here
    Print("Attempting to close all sell orders...");
    CloseAllSellOrders();
    Print("Finished attempting to close all sell orders.");
}

//+------------------------------------------------------------------+
//| Function to close all open sell orders                           |
//+------------------------------------------------------------------+
void CloseAllSellOrders()
{
    int total = OrdersTotal();
    Print("Total open/pending orders found: ", total);

    for(int i = total - 1; i >= 0; i--)
    {
        // Select the order by its index in the order pool
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            // Check if it's a sell order
            if(OrderType() == OP_SELL)
            {
                // Apply additional filters if specified
                bool matches_symbol = !OnlyCurrentSymbol || (OrderSymbol() == Symbol());
                bool matches_magic = (MagicNumber == 0) || (OrderMagicNumber() == MagicNumber);

                if(matches_symbol && matches_magic)
                {
                    int ticket = OrderTicket();
                    double lots = OrderLots();
                    double close_price = Bid; // Use Bid price to close a sell order

                    Print("Attempting to close Sell Order ", ticket, ", Symbol: ", OrderSymbol(), ", Lots: ", lots, ", Magic: ", OrderMagicNumber());

                    // Attempt to close the order
                    if(OrderClose(ticket, lots, close_price, Slippage))
                    {
                        Print("\tSell Order ", ticket, " close request sent successfully.");
                        // A small delay might be necessary in some brokers/situations
                        // Sleep(50); // Example: wait 50ms
                    }
                    else
                    {
                        Print("\tError closing sell order ", ticket, ". Error: ", GetLastError());
                    }
                } // End if(matches_symbol && matches_magic)
            } // End if(OrderType() == OP_SELL)
        } // End if(OrderSelect...)
        else
        {
            Print("Error selecting order at index ", i, ". Error: ", GetLastError());
        }
    } // End for loop
}

//+------------------------------------------------------------------+

Integrating the Function into an Expert Advisor or Script

  • Expert Advisor (EA): Place the CloseAllSellOrders() function definition anywhere outside of the standard event handler functions (OnInit, OnDeinit, OnTick, OnTrade, etc.). Call this function from within an event handler when your specific strategy logic dictates that all sell orders should be closed.
  • Script: Scripts execute sequentially from OnStart(). Place the function definition similarly, and call CloseAllSellOrders() directly within the OnStart() function to execute the closing logic once when the script is attached to a chart.

Remember that in an EA, you typically wouldn’t call this on every tick unless that’s the explicit (and likely inefficient) design. Trigger it based on conditions derived from indicators, price action, time, or other criteria.

Considerations for Magic Numbers and Symbols

The provided example includes parameters (MagicNumber, OnlyCurrentSymbol) to demonstrate how to make the function more flexible. If MagicNumber is set to a non-zero value, only sell orders with that specific magic number will be closed. If OnlyCurrentSymbol is true, only sell orders on the chart’s symbol will be considered.

For a general utility script intended to close all sell orders on the entire account, set MagicNumber = 0 and OnlyCurrentSymbol = false. For an EA managing its own trades, you would typically set MagicNumber to the EA’s unique number and OnlyCurrentSymbol to true.

Best Practices and Considerations

Slippage and Price Tolerance

When closing market orders, especially in volatile conditions, the execution price might differ from the requested price. The slippage parameter in OrderClose() defines the maximum acceptable deviation in points. Set a value that balances the need for execution against the risk of unfavorable pricing. A value between 5 and 15 points is common, depending on the currency pair and market conditions.

Order Closing Confirmation and Verification

OrderClose() is a synchronous function call that sends the trade request to the server. It returns true if the request is sent successfully, not necessarily if the order is closed immediately. In high-frequency scenarios or when immediate confirmation is critical, you might need to implement logic to verify the order’s status after calling OrderClose(), perhaps by re-selecting it or checking the OrdersTotal() count after a small delay.

Handling Trade Context Errors

Calls to trade functions like OrderClose() can sometimes fail due to temporary issues (e.g., server busy, invalid price). Checking GetLastError() provides the error code. Depending on the error, you might choose to retry the operation, wait and try again, or log the error and move on. The specific error code ERR_TRADE_CONTEXT_BUSY (code 4006) is common if multiple trade operations are attempted too quickly. Adding small delays (Sleep()) between trade operations, especially within a loop closing multiple orders, can help mitigate this.

Impact on Account Balance and Margin

Closing orders affects your floating profit/loss, which in turn updates your account balance (realized P/L) and equity. Closing positions also releases used margin, potentially increasing your free margin. Be mindful of the potential margin call risk if closing profitable positions significantly reduces your equity or if closing losing positions doesn’t release enough margin.

Implementing a function to close all sell orders is a fundamental MQL4 skill. By understanding order iteration, filtering, and the nuances of OrderClose(), developers can build robust trade management features into their automated systems.


Leave a Reply