Thursday, 6 June 2013

How To Write a Close All Script in MQL4?

How To Write a Close All Script in MQL4?

If you're just wanting to get a ready-made Metatrader script that closes all trades per chart or for the whole trading account, then follow the links and download the MQ4 script file of your choice:

There are many ways to write a script that closes all orders in MQL4, just as there are many ways to accomplish virtually anything via code. However, if you've taken the time to look at the code for any of the various Close All routines that can be found on the various sites on the WWW, you will notice some striking similarities. Before you can close all orders, you must first learn how to iterate through all the open orders and learn something about how MT4 (Metatrader 4) handles orders.

MQL4 has a simple way to iterate through all open orders for a given account by using two simple functions. The OrdersTotal() function help file states that it returns, "market and pending orders count." By orders, the creators of MT4 really mean orders such as stop orders, or trades such as buy or sell trades. The other little tidbit that the MT4 help file leaves out is that all orders and trades for the current account will be checked with this function. A simple loop to iterate through all orders and trades might look like the following:

for (int i = OrdersTotal(); i >=0; i--) {
    // Do something here
}

In the code above the (int)eger i is declared (int i = OrdersTotal();) or in other words i is set to the total open order count.. The line termination character in MQL4 is the semicolon. The loop continues while i >= 0 or in other words until no orders are left. When  OrdersTotal()  returns -1 there are no orders open. (This is an undocumented return value that you may want to make note of.) The integer i increments down by 1 each time (i--). This is the basics of a "for" loop. The variable names can change (i was used in this example) but "for" loops follow this basic pattern every time. Curly brackets start and end a block of code, in this case a for loop.

Next you need to select the orders by using the OrderSelect() function. This can be done as follows:

OrderSelect(i, SELECT_BY_POS, MODE_TRADES);

In the MQL4 code above, orders are selected one at a time, using i as the reference number. Orders are selected by position. The two ways to select an order are SELECT_BY_POS which will give the index of total open orders, starting at 1, and SELECT_BY_TICKET which will return the actual order ticket number. When SELECT_BY_POS is used, a optional pool index may be entered with the default being MODE_TRADES which returns order index numbers of open orders and trades, while MODE_HISTORY will return the index numbers of closed and cancelled orders.

If you wish to close all open orders, you should use MODE_TRADES, which will select trades by index number so SELECT_BY_POS is used for this example. Putting the code together, you have the following:

for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
}
 
MQL4 contains a simple way to differentiate between different types of orders. The order types that are of most concern when attempting to close all open trades are open buy and sell orders, represented by the OP_BUY and OP_SELL constants used in the OrderType() function. The OrderType() function simply returns the type of order that is currently selected. Returning back to the code, it is possible to create a Close All routine that only closes buy (or long) orders as follows:

for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    if (OrderType() == OP_BUY) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Bid, 5, White);
    }
}
In the MQL4 code above "closed" is a boolean (true/false) variable that is defined to accept the return value from the OrderClose() function. By selecting the orders with the OrderSelect() function, it is possible to make use of several other built-in MQL4 functions that make the job of closing an order much simpler.

The OrderClose() function defined in the code above takes the following variables:
    ticket - Refers to the order index number selected, in this case it will be i, but to keep the code generalizable it is best to use the OrderTicket() function.

    lots - May be referenced by the OrderLots() function which returns the number of lots open for a given ticket number.

    Bid - Refers to the current symbol's bid price. Unfortunately in the code sample provided, does not distinguish between the orders that refer to the current symbol and the orders that belong to other symbols so using the Bid price of the current symbol will often lead to errors due to prices not matching. 

    5 - Refers to the number of pips (or pippettes for 3/5 digit symbols) of slippage you are willing to allow.

    White - refers to a color that will be used to show the exit on the chart. Any valid color constant will work here. CLR_NONE may be used to instruct Metatrader not to draw a close object on the screen.

To make sure the code works for a single symbol you need to add an additional condition to the code as follows:
for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    if (OrderType() == OP_BUY && Symbol() == OrderSymbol()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Bid, 5, White);
    }
}

By specifying that Symbol() == OrderSymbol() you are requiring the current chart's symbol be equal to the symbol with the currently selected open order. Open orders of other symbols will not be closed in this manner. The code can be made to close both buy and sell orders as follows:
int Slippage = 5;
for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    if (OrderType() == OP_BUY && Symbol() == OrderSymbol()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Bid, Slippage, White);
    }
    if (OrderType() == OP_SELL && Symbol() == OrderSymbol()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Ask, Slippage, White);
    }
}
Additionally, a variable has been created for Slippage for reuse purposes. Note that the Ask price is used as the exiting price for sell orders as open sell orders must be bought back at the ask price in order to close. Likewise, open buy orders must be sold back at the bid price to close.

MT4 has some gotchas with regards to order placement that need to be taken into account. Often times the above code will cause MT4 to kick out errors when the program isn't expecting an order to be submitted due to the trade context being busy, or due to stale prices. 

Magic Numbers
You may not be familiar with the concept of a magic number as it is used in MT4. Magic Numbers are actually a very important concept to understand because Magic Numbers are found in many different types of MQL4 code, and can be integrated into a Close All routine simply. A Magic Number is a number used by an MT4 indicator, MT4 expert advisor or MT4 script to differentiate that MT4 expert advisor, MT4 indicator or MT4 script from one another being run on the same account. Perhaps the most useful way to think about a Magic Number is in terms of an MT4 expert advisor. If you are running multiple MT4 expert advisors or systems on a single account. If you want to keep track of the orders from a single strategy and differentiate them from another strategy running on the same account for the purposes of opening and closing orders (trades), and perhaps even on the same symbol, you could use a Magic Number.  The OrderSend() function is defined as follows:

int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)
Note the parameter "int magic = 0" which refers to a Magic Number that may be entered when an order is created. This same Magic Number may be referenced when closing an order. If you are running two different MT4 expert advisors on a single symbol, you could specify two different Magic Numbers in the OrderSend() function of each MT4 expert advisor, to ensure that each could handle its close order routine separately. Without using a unique Magic Number for each MT4 expert advisor, either Expert could close orders opened by a different MT4 expert advisor. This is usually not the intent of an MT4 expert advisor though in some cases it is desirable to close orders created by a different MT4 expert advisor.
The close all routine can be ammended to specify that only a given Magic Number is closed out with this routine. This can be accomplished as follows:
int Slippage = 5;
int MagicNumber = 321;
for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    if (OrderType() == OP_BUY && Symbol() == OrderSymbol() 
     && MagicNumber == OrderMagicNumber()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Bid, Slippage, White);
    }
    if (OrderType() == OP_SELL && Symbol() == OrderSymbol()
     && MagicNumber == OrderMagicNumber()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Ask, Slippage, White);
    }
}
Finally, MQL4 code can be added that ensures that the trade context is ready, and that the most recent prices are available. The MQL4 code has been placed in a function called CloseThis, because the routine just closes a single symbol's trades with the given Magic Number.

void CloseThis(int Slippage, int MagicNumber) {
for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    while(IsTradeContextBusy()) Sleep(100);
    RefreshRates();
    if (OrderType() == OP_BUY && Symbol() == OrderSymbol() 
     && MagicNumber == OrderMagicNumber()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Bid, Slippage, White);
    }
    if (OrderType() == OP_SELL && Symbol() == OrderSymbol()
     && MagicNumber == OrderMagicNumber()) {
        bool closed = OrderClose( OrderTicket(), OrderLots(), Ask, Slippage, White);
    }
}
}
The IsTradeContextBusy() function returns a boolean TRUE just as one would expect when some other EA is tying up the trade thread, and false when trading is available. Alternatively the function IsTradeAllowed() may be used as this function not only checks that the Trade context isn't busy, but also checks that trading is allowed for the expert.

The Sleep() function simply waits a given number of milliseconds before resuming with the next line of MQL4 code.

The RefreshRates() function forces the current prices to be updated. If you are sending orders and getting errors 135 (ERR_PRICE_CHANGED) or 138 (ERR_REQUOTE) you should use the RefreshRates() function to force new prices to update to your trade variables. It is a good practice to attempt to refresh rates during a close loop as closing orders may take time, during which time prices may shift causing errors.

The CloseThis() function may be called from any other MT4 function simply as follows for slippage of 7 pips and a magic number of zero:
CloseThis(7,0); 


MT4 contains a simple method for referencing the prices and data of different symbols in MQL4. This function is the MarketInfo() function. The MarketInfo() function has many uses, all of which will not be explored in this article. MarketInfo() can be used to retrieve the bid and ask prices of  the currently selected symbol as MT4 iterates through a list of orders and attempts to close them. This is a useful feature that is needed to make a CloseAll routine work correctly.

One additional gotcha with regards to MT4 is that sometimes rounding errors can cause problems that prevent orders from closing. The OrderClose() function is very particular about the type of data it receives, particularly the prices used, including the number of decimals in those prices. When referencing prices from a symbol other than the current symbol, it is good practice to round the symbol's price to the correct number of decimal places. A symbol's decimal places or Digits can be retrieved via the MarketInfo() function from the currently selected order symbol as follows:
MarketInfo(OrderSymbol(), MODE_BID); 
MarketInfo(OrderSymbol(), MODE_ASK);

To finish off these lines of MQL4 code, the NormalizeDouble() function can be used to round a price to a specified number of Digits, and in the case of MarketInfo(OrderSymbol(), MODE_DIGITS); to retrieve the digits from the selected order's settings. The code to close all orders for all symbols can be generalized as follows:

void CloseAll(int Slippage, int MagicNumber) {
bool closed;
for (int i = OrdersTotal(); i >=0; i--) {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    while(IsTradeContextBusy()) Sleep(100);
    RefreshRates();
    if (OrderType() == OP_BUY && MagicNumber == OrderMagicNumber()) { 
        closed = OrderClose(OrderTicket(),OrderLots(),
         NormalizeDouble(MarketInfo(OrderSymbol(),
         MODE_BID),MarketInfo(OrderSymbol(),MODE_DIGITS)),Slippage,White);
    }
    if (OrderType() == OP_SELL && MagicNumber == OrderMagicNumber()) { 
        closed = OrderClose(OrderTicket(),OrderLots(),
         NormalizeDouble(MarketInfo(OrderSymbol(),
         MODE_ASK),MarketInfo(OrderSymbol(),MODE_DIGITS)),Slippage,White);
    }
}
}
Just as with the CloseThis routine, the CloseAll routine may be called as follows using 7 pips of slippage and a Magic Number of zero:
CloseAll(7, 0);

Using the MQL4 code contained in this sample, you should now be able to write your own CloseAll or CloseThis routine. While not all advanced topics have been covered in this short tutorial, by making use of the MT4 help files and the other MQL4 code samples on this site and others, you should be able to build or adapt your very own Metatrader order closing routine!

If you would like to download the code for this tutorial, you may find the links here:
 

2 comments:

  1. I have bookmarked your online journal, the articles are path superior to anything other comparable web journals.. a debt of gratitude is in order for an extraordinary online journal! marketing

    ReplyDelete
  2. f you someday forget your password you can you this disk to reset your password and create a new one. Before you even iqoption.com loginstart with this you need to have 1 USB memory which you gone use to store a file which will reset a password. If you have all necessary items the fallow below steps:

    ReplyDelete