FX NOOKAI CODE LAB
Loading

MT4 EA Close All Positions: MQL4 Code and Checks

By FXNook Lab team

Trader reviewing an MT4 EA setup at a desk

To close open market trades from an MT4 EA, select each order in the trade pool with OrderSelect, then close buys with the symbol’s current Bid and sells with its current Ask using OrderClose. The MQL4 functions below filter by symbol and magic number, report failed closes, and keep pending-order deletion separate because pending orders must be deleted with OrderDelete.

Close market orders with an MT4 EA

MT4 exposes active market orders and pending orders through OrdersTotal() and the MODE_TRADES pool. Loop from the highest position down, select each order by position, and act only on OP_BUY and OP_SELL. The example returns the number of successful closes; failures are written to the Experts log with the ticket and error code.

// MQL4: place these functions in an Expert Advisor.
bool MatchesSelectedOrder(bool filterSymbol, string symbolFilter,
                          bool filterMagic, int magicNumber)
{
   if(filterSymbol && OrderSymbol() != symbolFilter)
      return false;
   if(filterMagic && OrderMagicNumber() != magicNumber)
      return false;
   return true;
}

int CloseMarketOrders(bool filterSymbol, string symbolFilter,
                      bool filterMagic, int magicNumber,
                      int slippagePoints)
{
   int closedCount = 0;

   for(int pos = OrdersTotal() - 1; pos >= 0; pos--)
   {
      if(!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES))
      {
         int selectError = GetLastError();
         Print("OrderSelect failed at pool position ", pos,
               "; error=", selectError);
         continue;
      }

      int type = OrderType();
      if(type != OP_BUY && type != OP_SELL)
         continue;

      if(!MatchesSelectedOrder(filterSymbol, symbolFilter,
                               filterMagic, magicNumber))
         continue;

      int ticket = OrderTicket();
      string orderSymbol = OrderSymbol();
      double lots = OrderLots();

      RefreshRates();
      double closePrice;
      if(type == OP_BUY)
         closePrice = MarketInfo(orderSymbol, MODE_BID);
      else
         closePrice = MarketInfo(orderSymbol, MODE_ASK);

      if(closePrice <= 0)
      {
         Print("No valid close price for ticket=", ticket,
               " symbol=", orderSymbol);
         continue;
      }

      ResetLastError();
      if(OrderClose(ticket, lots, closePrice, slippagePoints, clrNONE))
      {
         closedCount++;
      }
      else
      {
         int closeError = GetLastError();
         Print("OrderClose failed. ticket=", ticket,
               " symbol=", orderSymbol,
               " type=", type,
               " error=", closeError);
      }
   }

   return closedCount;
}

OrderClose needs the ticket, lots to close, close price, slippage in points, and an optional arrow colour. This example closes the full lot amount returned by OrderLots(). For a buy, the close is a sell at Bid; for a sell, the close is a buy at Ask. Passing the wrong side’s price is a common reason for a rejected close.

RefreshRates() refreshes the EA’s current chart-symbol rates. The code also gets the quote for the selected order’s own symbol with MarketInfo(orderSymbol, MODE_BID) or MODE_ASK, which matters when the function can close trades on symbols other than the chart symbol. The slippagePoints argument is in points, not pips; choose its value deliberately for the symbol’s digits and the EA’s execution rules.

What “all positions” means in MT4

MT4 calls both open market trades and pending instructions orders. OrderClose applies to an open buy or sell. A Buy Limit, Sell Limit, Buy Stop, or Sell Stop has not opened a market trade yet, so it cannot be closed with OrderClose. Delete a pending order with OrderDelete instead.

The first function handles market orders only. It deliberately skips every order type other than OP_BUY and OP_SELL, so running it will not remove pending orders. This distinction is useful when a close condition should flatten existing trades but leave planned entries in place.

The loop uses SELECT_BY_POS with MODE_TRADES to inspect the active pool. It walks backward because a successful close removes an item from that pool, changing the positions of entries after it. In a busy account, another EA or a manual action can still change the pool while the function runs, so read the return count and log and then inspect the remaining orders rather than assuming one pass handled every possible change.

Filter by symbol and magic number

Closing every market order in the account can affect manual trades and trades managed by other EAs. Use the filters to define exactly which orders belong to this close action. Set filterSymbol to true and pass Symbol() to restrict the function to the chart symbol; set filterMagic to true and pass the EA’s magic number to select only its orders.

For example, an EA that should close only its own EURUSD trades would call the function with both filters enabled, the symbol string "EURUSD", and the magic number assigned to those trades. To close all market orders on the current chart symbol regardless of who opened them, enable only the symbol filter. Disabling both filters means the function can close all market orders in the active account pool, so use that setting only when it is intentional.

Magic numbers are assigned when the EA opens an order, commonly as an argument to OrderSend. Manual trades usually have magic number zero, but an EA can also use zero if its code is written that way. Do not assume that a magic-number filter protects manual orders unless you know how the relevant orders were created; use both symbol and magic filters when both limits are needed.

Delete pending orders separately

Use a separate function for pending orders. It shares the same filters and backward traversal, but calls OrderDelete(ticket) and reports deletion failures. It will not close market buys or sells.

// MQL4: delete matching pending orders only.
int DeletePendingOrders(bool filterSymbol, string symbolFilter,
                        bool filterMagic, int magicNumber)
{
   int deletedCount = 0;

   for(int pos = OrdersTotal() - 1; pos >= 0; pos--)
   {
      if(!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES))
      {
         int selectError = GetLastError();
         Print("OrderSelect failed at pool position ", pos,
               "; error=", selectError);
         continue;
      }

      int type = OrderType();
      if(type != OP_BUYLIMIT && type != OP_SELLLIMIT &&
         type != OP_BUYSTOP && type != OP_SELLSTOP)
         continue;

      if(!MatchesSelectedOrder(filterSymbol, symbolFilter,
                               filterMagic, magicNumber))
         continue;

      int ticket = OrderTicket();
      ResetLastError();
      if(OrderDelete(ticket, clrNONE))
      {
         deletedCount++;
      }
      else
      {
         int deleteError = GetLastError();
         Print("OrderDelete failed. ticket=", ticket,
               " symbol=", OrderSymbol(),
               " type=", type,
               " error=", deleteError);
      }
   }

   return deletedCount;
}

Call this function only if the EA’s rules say pending orders should be removed too. A “close trades” rule often means closing open market orders, while a “cancel all entries” rule means deleting pending orders. If the intended action is to flatten the account and remove unfilled entries, call both functions with the same filters and handle their returned counts separately.

Call the close function only on a deliberate trigger

Place the call inside the EA event or rule that defines when closing is required, such as a basket-exit condition, a session-end rule, or a user-confirmed emergency action. Do not call it unconditionally on every tick: that can repeatedly send trade requests, fill the Experts log with errors, or close orders that the EA’s other rules expect to remain open.

In an existing OnTick(), the structure should be: evaluate the EA’s actual close condition, call CloseMarketOrders only when that condition is true, and call DeletePendingOrders only if the same action is meant to cancel pending entries. The exact condition belongs to the strategy; avoid replacing it with an always-true condition or a hard-coded trigger that could close trades unexpectedly.

After the call, use the returned count as a report of successful requests, not as proof that the account is flat. A close can fail, a new order can arrive, or the trade pool can change while the EA is running. If the EA needs to ensure no matching orders remain, run a fresh scan after the close attempt and report any matching market or pending orders still present.

Read OrderClose and OrderDelete failures

The functions return true when the server accepts the requested operation and false when the request fails. Calling ResetLastError() immediately before the trade request and GetLastError() immediately after a failure helps associate the code in the Experts log with that request. The error number is diagnostic information; it is not a reason to retry blindly on every tick.

Problem or errorWhat to inspectPractical response
Stale or rejected quote: errors 129, 136, or 138Price is invalid, off quotes, or requoted. Confirm the order symbol’s Bid/Ask is available and that the correct side is used.Refresh rates, get a fresh quote for the order symbol, and retry only under a controlled retry policy. Do not keep submitting the same old price.
Market closed: error 132The symbol may be outside its trading session or trading may be paused.Wait until the symbol can trade, then let the EA’s defined logic make another attempt. Record that the order remains open meanwhile.
Trade context busy: error 146Another trade request may be in progress in the terminal.Avoid overlapping trade operations. Schedule a later attempt rather than spinning through repeated requests in the same tick.
Trading disabled: error 133Trading may be disabled for the symbol, account, terminal, or server.Inspect the terminal’s AutoTrading setting, the EA’s live-trading permission, account permissions, and broker restrictions.
Other broker rejectionRead the error number in the Experts log and check the selected ticket, order type, symbol, lots, and current trading conditions.Use the matching MQL4 error description to identify the cause. Do not report success or discard the ticket just because the function was called.

MetaEditor and the terminal’s Experts and Journal tabs provide different parts of the diagnosis. A successful compile confirms that the code is syntactically acceptable for MQL4, but it does not show whether a broker will accept a close request or whether the filters select the orders you intended. Log the ticket, symbol, order type, and error as in the examples, then compare those details with the live order and the EA’s trigger.

Test the close logic in MT4

  1. Compile in MetaEditor. Save the functions in the EA and compile with the intended MT4 MetaEditor. Resolve compile errors before testing; if the code is part of a larger EA, confirm that the helper function names do not conflict with existing functions.
  2. Test the trigger in Strategy Tester. Use the MT4 Strategy Tester to reach the EA condition that calls the close function. Verify that it is not called on every tick and that the EA’s symbol and magic-number inputs match the test orders.
  3. Test each order type. Exercise a buy and a sell and confirm that the requested close uses Bid for the buy and Ask for the sell. If pending-order deletion is enabled, test each pending type and confirm that market orders are not deleted or confused with pending entries.
  4. Test the filters. Create or identify orders with a different symbol and a different magic number. With both filters on, only the matching orders should be selected; then test the intended symbol-only or magic-only mode separately.
  5. Inspect partial failures and logs. Read the Experts and Journal tabs for successful operations, error numbers, and any matching orders left open. A partial failure means the EA must report what remains, not assume the first successful close flattened everything.
  6. Repeat on a demo account. Test under the broker’s symbol settings and permissions before using the code in a live environment. Past or tested results do not guarantee future results, and execution conditions can differ between testing and live trading.

Next step

Start by adding the market-order function to the EA and calling it only from a clearly defined close condition. Test the filters and log output in Strategy Tester, then on a demo account; if you are building an EA from confirmed trading rules, the FXNook AI EA builder creates MT4 or MT5 EAs that are compiled and checked.

Questions

How do I close all open trades from an MT4 EA?

Loop through the active order pool with `OrderSelect`, identify market orders with `OP_BUY` or `OP_SELL`, and close them with `OrderClose`. Use Bid to close a buy and Ask to close a sell, and check the function’s return value and error log for each request.

Does OrderClose also delete pending orders in MT4?

No. `OrderClose` closes an open market buy or sell, while pending orders are removed with `OrderDelete`. Decide whether the EA’s action should close existing trades, delete unfilled entries, or do both.

How can I close only trades with a specific magic number?

After selecting each order, compare `OrderMagicNumber()` with the magic number assigned by your EA and skip orders that do not match. Apply the same comparison before calling `OrderClose`; you can also filter by `OrderSymbol()` to limit the action to one symbol.