MQL4 - automated forex trading   /  

MQL4 Book

Login | Register | Language: ru cn en
Buy financial books in MetaTrader Market and trade with confidence

MQL4 Book  Programming of Trade Operations  Opening and Placing Orders

Upgrade to
Book in One File

Opening and Placing Orders


Trade requests for opening and placing pending orders are formed using the function OrderSend().

Function OrderSend()

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)

(please note that here and below, we refer to function header, not to an example of how to use function call in a program).

Let's consider in more details what this function consists of.

OrderSend is the function name. The function returns the ticket number ('ticket' is the unique number of an order) that is assigned to the order by the trade server, or -1, if the trade request has been rejected by either the server or the client terminal. In order to get information about the reasons for rejection of the trade request, you should use the function GetLastError() (below we will consider some of the most common errors).

symbol is the name of the traded security. Each symbol corresponds with the value of a string variable. For example, for the currency pair of Euro/US dollar, this value is "EURUSD". If the order is being opened for a foregone symbol, then this parameter can be specified explicitly: "EURUSD", "EURGBP", etc. However, if you are going to use the Expert Advisor in the window of any other symbol, you can use the standard function Symbol(). This function returns a string value that corresponds with the name of the symbol, in the window of which the EA or script is being executed.

cmd is the type of operation. The type of operation can be specified as a predefined constant or its value, and according to the type of the trade.

volume is the amount of lots. For market orders, you must always check the account for the sufficiency. For pending orders, the amount of lots is not limited.

price is the open price. It is specified according to the requirements and limitations accepted for making trades (see Order Characteristics and Rules for Making Trades). If the price requested for opening of market orders has not been found in the price thread or if it has considerably outdated, the trade request is rejected. However, if the price is outdated, but present in the price thread and if its deviation from the current price ranges within the value of slippage, this trade request will be accepted by the client terminal and sent to the trade server.

slippage is the maximum allowed deviation of the requested order open price from the market price for market orders (points). This parameter is not processed for placing of pending orders.

stoploss is the requested close price that determines the maximum loss allowed for the given trade. It is set according to the requirements and limitations accepted for making trades (see Order Characteristics and Rules for Making Trades, Requirements and Limitations in Making Trades).

takeprofit is the requested close price that determines the maximum profit for the given trade. It is set according to the requirements and limitations accepted for making trades (see Order Characteristics and Rules for Making Trades, Requirements and Limitations in Making Trades).

comment is the text of the order comment. The last part of the comment can be modified by the trade server.

magic is the magic number of the order. It can be used as the user-defined order identifier. In some cases, it is the only information that helps you to find out about that the order belongs to one or another program that has opened it. The parameter is set by the user; its value can be the same or other than the value of this parameter of other orders.

expiration is the date when the order expires. As soon as this day comes, the pending order will be closed automatically on the server side. On some trade servers, there may be a prohibition for setting the expiration date for pending orders. In this case, if you try to set a non-zero value of the parameter, the request will be rejected.

arrow_color is the color of the opening arrow in the chart. If this parameter is absent or if its value is CLR_NONE, the opening arrow is not shown in the chart at all.

On some trade servers, there can be a limit set for the total amount of opened and pending orders. If this limit is exceeded, any trade request that implies opening a market order or placing a pending order will be rejected by the trade server.


Opening Market Orders


The function OrderSend() may at first seem to be too intricate. However, all considered parameters are quite simple, helpful and can be successfully used in your trading. In order to see this for ourselves, let's consider the simplest variation of how the trade function OrderSend() is used for opening a market order.

First of all, we should note that function OrderSend() has predefined parameters (see Function Call and Function Description and Operator 'return'). This means that this function can be used in a simplified mode using the minimum required set of parameters. These parameters are as follows:

symbol is a necessary parameter, because we need to know where to open the order. Let our script imply the possibility to open an order in any symbol window. In this case, we will substitute the standard function Symbol() as this parameter;

cmd - for example, let's open a Buy order; in this case, we will specify parameter OP_BUY;

volume - we can specify any value allowed by the rules; let's open a small order, for example, of 0.1 lot;

price - open price for the order Buy is price Ask;

slippage is usually specified as 0-3 points. Let's specify 2;

stoploss - stop orders can be placed at a distance that is not closer than the minimum allowed distance, normally 5 points (see Requirements and Limitations in Making Trades); let's place stop orders at a distance of 15 points from the close price, namely: Bid - 15*Point;

takeprofit - let's place stop orders at a distance of 15 points from the close price, namely: Bid + 15*Point;

Below is the simplest script, simpleopen.mq4, that is intended for opening a Buy order:

//--------------------------------------------------------------------
// simpleopen.mq4
// The code should be used for educational purpose only.
//--------------------------------------------------------------------
int start() // Special function start()
{ // Opening BUY
OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);
return; // Exit start()
}
//--------------------------------------------------------------------

If you launch this script for execution, it will work, in the majority of cases. The script consists of one special function that contains the order-opening function OrderSend() and the operator 'return'. Let's describe the execution algorithm for program lines and events related to that.

1. The user has attached the script to the symbol window by dragging the script name with the mouse button from the "Navigator" window of the client terminal into the window of the symbol, for which he or she wants to open a market order Buy of 0.1 lot and with stop orders that are at a distance of 15 points from the market price.

2. At the moment of attaching the script to the symbol window, the client terminal is passing the control (just by launching it) o the special function start() (here we should briefly remind that the start() of a script is launched at the moment of attaching the script to the symbol window, whereas the start() of an EA is launched at the moment when the nearest tick incomes for the symbol).

3. Within the framework of execution of the special function start(), the control is passed to the line that calls the order opening function:

   OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);

Before execution of this function, the program calculates the values of all formal parameters:

3.1. We attached the script to the window of Eur/USd.In this case, the standard function Symbol() will return the string value EURUSD.

3.2. Let Ask =1.2852 and Bid =1.2850 as of the moment of calling this function.

3.3. The value of StopLoss, in this case, will be: 1.2850-15*0.0001 = 1.2835, whereas TakeProfit = 1.2865.

4.Execution of the function OrderSend():

4.1. The function formed a trade request for opening of an order and passed this request to the client terminal.

4.2. The function passed the control to the client terminal simultaneously with passing of the trade request, so the program execution was stopped.

4.3. The client terminal checked the received trade request. It didn't detect any incorrect parameters, so it sent the request to the server.

4.4. The server received the trade request, checked it, didn't detect any incorrect parameters, and decided to execute the request.

4.5. The server executed the request by making a transaction in its database and sent the information about that executed request to the client terminal.

4.6. The client terminal received the information about that the last trade request had been executed, displayed this event in the terminal window and in the symbol window, and returned the control to the program.

4.7. Once having received the control, the program continued working from the location, from which the control had previously been passed to the client terminal (and to which it had been returned later).

Please note that no actions were performed in the program starting from step 4.2 through step 4.7 - the program was in the mode of waiting for the server response.

5. The control in the program is passed to the next operator - the operator 'return'.

6. The execution of the operator 'return' results in exiting the function start() and, therefore, in termination of the program execution (it should be reminded that scripts complete their work after they have being executed) - the control is returned to the client terminal.

Thus, the script has fulfilled its intended purpose: order Buy with the preset parameters is opened. The use of scripts is very convenient, if you need to perform a small one-time operation; in this case, the use of a script is quite reasonable. According to step 4.6., the trader can see the order in the screen.


Fig. 81. Order placed by script simpleopen.mq4.

The events are not always ordered as shown above. It is possible that the trade request is rejected by the client terminal or by the server. Let's try to make some experiments, for example, change the symbol name: specify "GBPUSD" explicitly (this is quite allowable). We will obtain a program with the limited field of use:

int start()                               // Special function start
{ // Opening BUY
OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);
return; // Exit start()
}

Let's launch the script in the same window of symbol Eur/Usd. The script was intended to open an order in the window of Gbp/Usd. However, after it had been attached to the window of Eur/Usd, no order was opened in the window of Gbp/Usd.

A disadvantage of such programs is their functional limitation. In this case, once having attached the script to the symbol window, the user is just waiting for order opening. However, the order is not opened. The user is not aware of the reason why it is so: either it is caused by an algorithmic error in the program code or the trade request is "lost" by the way to the server, or the trade request has been rejected by the client terminal long time ago (thought the user is still waiting), or there is another reason.

In order to provide the user (and, which is also very important, the program) with the information about the events related to the execution of the trade request, it is necessary to process the errors.

Error Processing


A very important property of the client terminal is that, if an error occurs during the execution of an application, the client terminal does not stop the execution of the program. Errors are usually caused by the imperfection of the algorithm used in the application. In some cases, errors are caused by some external (as related to the program) factors. The internal causes of errors are any violations of the MQL4 requirements or of trading rules, for example, using invalid prices. The external causes are those that are not related to the application program, for example, interrupted connection.

If an error occurs at the execution of a program, the program will continue running, while the client terminal will generate the error code value available to the program through the function GetLastError().

Function GetLastError()

int GetLastError()

The function returns the code of the newly occurred error, then the value of special variable last_error that stores the code of the last error will be zeroized. The subsequent GetLastError() call will return 0.

Hereinafter, we will identify all occurring errors by this code. Several errors can occur during the execution of a program; function GetLastError() allows us to get the code value for only one of them, the latest error, this is why every time when we need this information, it is recommended to use function GetLastError() immediately after the program lines, in which the error may occur.

Error 130. Invalid Stop Orders


The last considered script does not analyze errors, so the user remains ignorant about the results of the execution of order-opening function. In the simple variation of using the function GetLastError(), the program can analyze an error and just inform the user about it. If you launch script confined.mq4 for execution in the window of Eur/Usd, an error will occur.

//--------------------------------------------------------------------------
// confined.mq4
// The code should be used for educational purpose only.
//--------------------------------------------------------------------------
int start() // Special function start
{ // Opening BUY
OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);
Alert (GetLastError()); // Error message
return; // Exit start()
}
//--------------------------------------------------------------------------

We added only one, but very informative line into this script:

   Alert (GetLastError());                      // Error message

Function GetLastError() returns the code of the last error, whereas Alert() is used to display this value on the screen. After script confined.mq4 has been attached to the window of symbol Eur/Usd, the script will be executed, which will result in that the user will see the following message:


Fig. 82. Error code obtained at the execution of script confined.mq4 in eur/usd window.

You can find in Appendixes codes of errors that can occur at the execution of a program. In this case, error 130 (invalid stop orders) occurred. This means that the values of formal parameters used in the function OrderSend() don't comply with the limitations specified in Requirements and Limitations in Making Trades. Upon a closer view, we can see the reason that caused the error: the current values of market prices Bid and Ask are taken from the symbol window, to which the script is attached, namely, from the window of Eur/Usd. However, these values are used to form a trade request for Gbp/Usd. As a result, at the current price of Gbp/Usd, Ask = 1.9655, the value of TakeProfit for the newly opened market order turns out to be equal to (for Eur/Usd Bid =1.2930) 1.2930+15*0.0001=1. 2945, which is considerably lower than the minimum allowed value, i.e., it is invalid.

In this case, an algorithmic error occurred. In order to correct it, you should use the correct values of symbol prices. You can obtain these values using the function MarketInfo(). Script improved.mq4 that opens market orders for Gbp/Usd can be launched in any symbol window:

//------------------------------------------------------------------------------
// improved.mq4
// The code should be used for educational purpose only.
//------------------------------------------------------------------------------
int start() // Special function start
{
double bid =MarketInfo("GBPUSD",MODE_BID); // Request for the value of Bid
double ask =MarketInfo("GBPUSD",MODE_ASK); // Request for the value of Ask
double point =MarketInfo("GBPUSD",MODE_POINT);//Request for Point
// Opening BUY
OrderSend("GBPUSD",OP_BUY,0.1,ask,3,bid-15*Point,bid+15*Point);
Alert (GetLastError()); // Error message
return; // Exit start()
}
//------------------------------------------------------------------------------

The above error does not occur at the execution of this script, so its execution will result in displaying the corresponding message: 0 (zero). This means that the function GetLastError() returned the value of 0, i.e., no errors were detected in the execution of the trade request by the client terminal.

Let's also consider some other common errors. For this, let's return to the idea of opening an order using a script in the same window, to which the script is attached.

Error 129. Invalid Price


In some cases, a simple error occurs - the wrong value of the two-way quote is specified as the open price. Market orders Buy are known (see Requirements and Limitations in Making Trades) to be opened at the Ask price. Below is shown what happens if we, by mistake, specify the Bid price in script mistaken.mq4:

//-------------------------------------------------------------------------
// mistaken.mq4
// The code should be used for educational purpose only.
//-------------------------------------------------------------------------
int start() // Special function start
{ // Opening BUY
OrderSend(Symbol(),OP_BUY,0.1,Bid,3,Bid-15*Point,Bid+15*Point);
Alert (GetLastError()); // Error message
return; // Exit start()
}
//-------------------------------------------------------------------------

Before sending the trade request to the server, the client terminal analyzes whether the requested values of price and stop orders comply with the allowed values. During this check, the requested open-order price will be detected as invalid, so the client terminal will not send the trade request to the server for execution, and function GetLastError() will return the value of 129 (see Error Codes). The execution of the script will result in appearance of the corresponding error message:


Fig. 83. Error 129 (invalid price) at the execution of mistaken.mq4.


Error 134. Not Enough Money for Making a Trade


A similar result (error 134) will be obtained, if there are not enough free money on the account to open an order. You can know about the amount of free money required to open 1 lot for buying of each symbol using the function MarketInfo(symbol_name, MODE_MARGINREQUIRED).

The size of one standard lot for the same symbol may vary in different dealing centers.

The required amount of free assets for opening a one-lot order is inversely proportional to the amount of the provided leverage. At the same time, the cost of 1 point in the deposit currency for a symbol does not relate to the provided leverage.

Table 3. Possible combinations of 1-lot cost and 1-point cost (deposit currency is US dollar).


Dealing Center 1 Dealing Center 2 Dealing Center 3
Buy Sell 1pt Buy Sell 1pt Buy Sell 1pt
EUR/USD 1296.40 1296.20 10.00 1296.50 1296.20 10.00 1000.00 1000.00 10.00
GBP/USD 1966.20 1966.00 10.00 1376.48 1376.20 7.50 1000.00 1000.00 10.00
AUD/USD 784.40 784.20 10.00 1569.20 1568.40 20.00 1000.00 1000.00 10.00
USD/JPY 1000.00 1000.00 8.29 1000.00 1000.00 8.29 1000.00 1000.00 8.29
USD/CHF 1000.00 1000.00 8.02 1000.00 1000.00 8.02 1000.00 1000.00 8.02
EUR/CHF 1296.40 1296.20 8.02 1296.35 1296. 35 8.02 1000.00 1000.00 8.02

Prices are given as of 16.12.2007.

Let's briefly consider some common methods of calculating the cost of 1 lot and of 1 point.

Dealing Center 1 (most common)

For the symbols that have USD reciprocally, the cost of 1 lot is equal to the current price of the corresponding two-way quote multiplied by 1000, whereas the cost of 1 point is equal to $10.

For the symbols that have USD as their numerator, the cost of 1 lot is equal to $1000.00, whereas the cost of 1 point is inversely proportional to the current quote and equal to 1/(Bid). For example, for USD/CHF, at Bid= 1.2466, the cost of 1 point is 1/1. 2466 = 8.02.

For cross rates, the cost of 1 lot is calculated in the same way as that of the numerator currency, whereas the cost of 1 point is calculated in the same way as that for the denominator currency. For example, for EUR/CHF, the cost of 1 lot is 129.40 (as for EUR/USD), whereas the cost of 1 lot is 8.02 (as for USD/CHF).

Dealing Center 2

In some dealing centers, considering the same rule of calculating costs, the values of costs can be different for some symbols. For example, the cost of 1 lot and the cost of 1 point may be proportionally increased or decreased. For example, this factor can be 0.75 for GBP/USD, whereas it is 2.0 for AUD/USD. Such representation of cost values does not result in any economical changes; in such cases, you just have to consider this special feature when calculating costs of your orders. You should also pay attention to the fact that the 1-lot costs for buying and selling of assets at cross rates are the same.

Dealing Center 3

there are also dealing centers that set the cost of 1 lot as $1000.00 for any symbol. At the same time, the cost of 1 point remains proportional to the current prices. This implies setting a special leverage for each symbol.

1-point cost of all symbols that are not quoted as related to USD always changes proportionally to the cost of the symbol specified reciprocally.

Generally, there can exist other principles of building cost values. It is needless to say that, prior to start real trading, you should find out about the calculation method for any specific dealing center and consider this method in your coding.

Free Margin

At coding, it is very important to consider the principle of forming free assets. Free margin (assets) is the amount of money that is available for making trades.

Let's consider an example. Let Balance be 5000.00, there are no open orders in the terminal. Let's open a Buy order of 1 lot in dealing center 3. The following rule is stated in dealing center 3:

If differently directed market orders are opened for one symbol, the smaller integrated cost of one-direction orders is released for trading and increases the amount of free assets (this rule is not applicable for all dealing centers).

The terminal window will display the information about the opened order. Please note that the margin makes 1000.00, order profit is -30.00, therefore the amount of free assets (free margin) makes 5000-1000-30=3970.00:


Fig. 84. Order Buy in the terminal window.

After a Sell order of the same value has been opened, free margin will increase. The smaller integrated cost of one-direction market orders makes 1000.00, so the free margin will increase by 1000.00. In Fig. 85, you can see the situation where the differently directed orders cost the same value, so the entire sum of orders costs is released for trading.


Fig. 85. Orders Buy and Sell in the terminal window.

After a Sell order of smaller cost has been opened, free margin will increase, as well. In this case, the smaller integrated cost of one-direction market orders makes 700.00, so the free margin will increase by 700.00, whereas the margin makes the difference between the integrated costs of differently directed orders (Fig. 86).


Fig. 86. Orders Buy and Sell in the terminal window.

If one more order Sell of 0.1 lot is opened (cost 100.00), the smaller integrated cost of one-direction market orders makes 700.00 + 100. 00 = 800.00. Thus, the margin (as compared to the situation where only one order Buy is opened) decreases by 800.00. As compared to the situation shown in Fig. 86, the margin decreases, whereas the equity increases by 100.00 (see Fig. 87).


Fig. 87. Orders Buy and Sell in the terminal window.

Free Margins shown in Fig. 86 and Fig. 87 differ from each other by more than 100.00, since the integrated profit of open orders has changed with change in the current price (the difference makes 8.00).

If we make similar manipulations in another dealing center, it's easy to see that the above order of forming the value of free margin is not kept. For some dealing centers, the following rule is effective:

Opening of any market orders does not release the equity or increase the free margin. Opening of market orders increases the equity by the amount that exceeds the integrated cost of differently directed market orders for a symbol (the rule does not apply in all dealing centers).

For example, if you have previously opened an order Buy of 4 lots for USD/JPY in dealing center 2, the amounts of equity and free margin will not change at opening of a 4-lot Sell order.


Fig. 88. The presence of differently directed orders does not release equity.

You can make calculations to know whether the current equity is enough for opening of an order. You can also use the function AccountFreeMarginCheck() that returns the value of free margin to remain after opening of a market order with certain amount of lots for a certain symbol. If the returned value is equal or more than 0, there are enough money on the account. If it is less than 0, then the order of this volume and for this symbol cannot be opened, the client terminal will return error 134.

In order to know the conditions offered by the dealing center and the amount of free margin required for opening of an order with the volume of 1 lot, you can use a simple script, conditions.mq4:

//--------------------------------------------------------------------------
// conditions.mq4
// The code should be used for educational purpose only.
//--------------------------------------------------------------------------
int start() // Special function start
{
Alert(Symbol()," Sell = ",AccountFreeMargin()// At selling
-
AccountFreeMarginCheck(Symbol(),OP_SELL,1));
Alert(Symbol()," Buy = ",AccountFreeMargin() // At buying
-
AccountFreeMarginCheck(Symbol(),OP_BUY,1));
return; // Exit start()
}
//--------------------------------------------------------------------------

Here, the expression of

AccountFreeMargin() - AccountFreeMarginCheck(Symbol(),OP_SELL,1)

allows us to calculate the difference between the available free margin and the free margin that will remain after opening of the order.

If we start this script for execution, when there are no market orders in the terminal, we can obtain the currently required amount of equity to be available and enough for opening of an order with the volume of 1 lot for buying and for selling:


Fig. 89. 1-Lot cost for different symbols, obtained using conditions.mq4.

If we launch the script conditions.mq4 for execution in the window of the symbol, for which there are opened market orders, we can obtain other values, it depends on the calculation methods accepted in one or another dealing center.

Other Errors and Function MarketInfo()


There are other limitations related to determining of values of parameters of function OrderSend(). This are the maximum and the minimum order price step, the maximum and the minimum order price value, etc. The use of function MarketInfo() allows you to get various information about symbols that are shown in the window "Market Watch" of the client terminal.

Function MarketInfo()

double MarketInfo(string symbol, int type)

The function returns various information about symbols listed in the window "Market Watch" of the client terminal. Parts of information about the current symbol are stored in predefined variables.

Parameters:

symbol - the name of a symbol;

type - request identifier that determines the type of information to be returned. It can be either value of those of request identifiers (see Function MarketInfo Identifier).

Some errors may occur for the reasons on the server side. For example, in the conditions of transient prices, your broker may increase the minimum distance that limits placement of pending orders and stop orders. Further, at a calm market, the broker can decrease this distance again. Thus, the values of some parameters can be changed at any time.

For the program to operate in a stable manner, with the minimal amount of rejected requests, you should update the parameters of information environment used by the program using the functions MarketInfo() and RefreshRates() before you execute the function OrderSend().

An example of a simple script that opens a Buy order costing 35% of the free margin, with some preset values of stop orders (openbuy.mq4).

//-------------------------------------------------------------------------------
// openbuy.mq4
// The code should be used for educational purpose only.
//-------------------------------------------------------------------------- 1 --
int start() // Special function start
{
int Dist_SL =10; // Preset SL (pt)
int Dist_TP =3; // Preset TP (pt)
double Prots=0.35; // Percentage of free margin
string Symb=Symbol(); // Symbol
//-------------------------------------------------------------------------- 2 --
while(true) // Cycle that opens an order
{
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Min. distance
double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Min. volume
double Step =MarketInfo(Symb,MODE_LOTSTEP);//Step to change lots
double Free =AccountFreeMargin(); // Free Margin
double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Cost per 1 lot
//-------------------------------------------------------------------- 3 --
double Lot=MathFloor(Free*ProtsOne_LotStep)*Step;// Lots
if (Lot < Min_Lot) // If it is less than allowed
{
Alert(" Not enough money for ", Min_Lot," lots");
break; // Exit cycle
}
//-------------------------------------------------------------------- 4 --
if (Dist_SL < Min_Dist) // If it is less than allowed
{
Dist_SL=Min_Dist; // Set the allowed
Alert(" Increased the distance of SL = ",Dist_SL," pt");
}
double SL=Bid - Dist_SL*Point; // Requested price of SL
//-------------------------------------------------------------------- 5 --
if (Dist_TP < Min_Dist) // If it is less than allowed
{
Dist_TP=Min_Dist; // Set the allowed
Alert(" Increased the distance of TP = ",Dist_TP," pt");
}
double TP=Bid + Dist_TP*Point; // Requested price of TP
//-------------------------------------------------------------------- 6 --
Alert("The request was sent to the server. Waiting for reply..");
int ticket=OrderSend(Symb, OP_BUY, Lot, Ask, 2, SL, TP);
//-------------------------------------------------------------------- 7 --
if (ticket>0) // Got it!:)
{
Alert ("Opened order Buy ",ticket);
break; // Exit cycle
}
//-------------------------------------------------------------------- 8 --
int Error=GetLastError(); // Failed :(
switch(Error) // Overcomable errors
{
case 135:Alert("The price has changed. Retrying..");
RefreshRates(); // Update data
continue; // At the next iteration
case 136:Alert("No prices. Waiting for a new tick..");
while(RefreshRates()==false) // Up to a new tick
Sleep(1); // Cycle delay
continue; // At the next iteration
case 146:Alert("Trading subsystem is busy. Retrying..");
Sleep(500); // Simple solution
RefreshRates(); // Update data
continue; // At the next iteration
}
switch(Error) // Critical errors
{
case 2 : Alert("Common error.");
break; // Exit 'switch'
case 5 : Alert("Outdated version of the client terminal.");
break; // Exit 'switch'
case 64: Alert("The account is blocked.");
break; // Exit 'switch'
case 133:Alert("Trading forbidden");
break; // Exit 'switch'
default: Alert("Occurred error ",Error);// Other alternatives
}
break; // Exit cycle
}
//-------------------------------------------------------------------------- 9 --
Alert ("The script has completed its operations ---------------------------");
return; // Exit start()
}
//-------------------------------------------------------------------------- 10 --

The script consists of one special function start() (blocks 1-10). In block 1-2, the values are set, at which the order must be opened. Block 2-9 represents cycle operator while(), in which all necessary calculations are performed. This cycle is included into the code to allow the program make several attempts to open the order. In block 2-3, the environment variables are updated. In blocks 3-4-5-6, the amount of lots and the requested prices of stop orders are calculated. In block 7-8-9, errors are processed. In block 9-10, the message is printed that the script has completed its operations.

Let's consider some special features of a program code. It's easy to see that the trade request is formed in block 6-7. In block 3-4, the amount of lots is calculated. It also considers the situation when the available free margin is insufficient to open even an order with the minimum amount of lots. This is why, in block 3-4, after printing the message about insufficient money, we exit cycle 2-9 using the operator 'break'. The control is passed to block 9-10, and script completes its operations. The message in block 9 is unnecessary. It is given here just to help users of the code to find tails or heads in the script - when is the end of the program's operations and when is the pause caused by delays in the network or on the server.

If the free margin is sufficient for opening of the order, the control will be passed to block 4-5 and then to block 5-6. In those blocks, there is no cycle exit. This means that, for any minimum distance set by the broker, there will be corresponding stop levels found. In block 1-2, 3 points were chosen for TP by design. The majority of brokers set the minimum distance as 5 points. In block 5-6, the program will discover that the preset value is less than the allowed one. The program will set such a value of the stop-order price that does not contradict the limitation.

then the control is passed to block 6-7 to open an order. In the first line of this block, the message is printed. The trade request is formed only in the second line. A question arises: Why do we declare about forming a request before it is really formed? We could give the instruction first and then inform the user about it. The answer to this question is closely related to the technology of sending the request to the client terminal and then to the server (see Fig. 66). In our case, the trade request is formed in the function OrderSend() specified in the right part of the assignment operator. The trade request as such is created and sent to the server in the function, whereas the assignment operation will be executed in the assignment operator after the server has returned a reply about the "fate" of the request. Thus, the only possibility to inform the user about the start of events related to the request is to show the message before the assignment operator, in the right part of which the trade function is specified.

Sooner or later, the client terminal will pass the control back to the program, the assignment operator in block 6-7 will be executed, which will result in that the 'ticket' variable will take a value, and the control will be passed further - to error-analyzing block 7-8-9.

If the order is opened on the server, the number (ticket) of the opened order will be assigned to the variable 'ticket'. In this case, it means that the script has fulfilled its task and there is no need for the program to continue operations. In block 7-8, we use the operator 'break' to exit cycle while(). The control is passed to block 9-10 (outside the cycle), and the program completes its operations.

However, if the attempt to open an order fails, the control will be passed to block 8-9 for error analyzing. Two types of errors are considered here: those that still allow to hope for successful opening of the order and those, the occurrence of which means unambiguous termination of the program execution. The variable 'Error' is assigned with the code of the last error, in this case, of the error that has been returned by the server or by the client terminal at execution of function OrderSend().

In the first operator 'switch' of block 8-9, overcomable errors are considered. Each error in this group is processed differently. For example, if the price has changed (error 135), it is sufficient just to update the environment parameters using RefreshRates() and repeat the attempt to open an order. If the error "No prices" (error 136) occurs, there is no sense to re-send the request to the trade server. In this case, we should wait for a new tick to income (there are no prices on the server at this time, either) and, only after that, retry to open an order. This is why there is a waiting cycle in the block that processes error 136. This waiting cycle will be interrupted as soon as a new tick incomes. We exit the operator switch() using operator 'continue' that breaks the current iteration of the cycle while() and starts a new one.

Critical errors are processed in another way. If such an error occurs, the program will just inform the user about it and terminate operations. For this purpose, we use the operator 'break' (the last one in block 8-9) that breaks the cycle while(), which results in termination of the program.

We should note particularly that, in this example, we don't consider all errors without exceptions, by design. In this case, we are not aiming at providing the user with a ready-made program. It is very important that the programmer him or herself analyzes other errors and decides independently what else errors and in what way should be processed in the program. At the same time, some errors must not be processed, because the program is built in such a way that it does not imply occurrence of some errors, for example, in this case, of errors 129 and 130..

In the above example, there is a small algorithmic error that cannot be found at neither compilation nor in the client terminal, nor on the server.

Take any program codes with a grain of salt, in contempt of any authorities.

Note the code in block 4-5:

//--------------------------------------------------------------------------- 4 --
if (Dist_SL<Min_Dist) // If it is less than allowed.
{
Dist_SL=Min_Dist; // Set the allowed
Alert(" Increased the distance of SL = ",Dist_SL," pt");
}
double SL = Bid - Dist_SL*Point; // Requested price of SL
//--------------------------------------------------------------------------- 5 --

As a result of calculations in the body of the operator if(), the variable Dist_SL can take a new value. Suppose a normal minimum distance makes 5 points. Suppose that at the first execution (in quick market), this value is set as 20 points on the server. The variable Min_Dist will take the value of 20.

      int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimum distance

Also suppose that the formed trade request has been rejected due to error 136. The program will track the new tick in block 8-9. Within this period of time, the value of the minimum distance can be changed on the server, for example, decreased to 10 points. At the moment when the new tick incomes, the control will be passed to the new cycle, and the new value of the variable Min_Dist, equal to 10, will be calculated. However, the value of the variable Dist_SL remains unchanged and equal to 20 (block 4-5 is coded in such a way that the value of Dist_SL can only increase). In order to exclude this algorithmic error, you should write block 4-5 in such a manner that only the value that depends on the situation would change (in this case, it is the value of SL), whereas the value of Dist_SL wouldn't change, for example, like this:

//------------------------------------------------------------------------- 4 --
double SL = Bid - Dist_SL*Point; // Requested price of SL
if (Dist_SL<Min_Dist) // If it is less than allowed
{
SL = Bid - Min_Dist*Point; // Requested price of SL
Alert(" Increased the distance of SL = ",Min_Dist," pt");
}
//------------------------------------------------------------------------- 5 --

A similar change must be made in block 5-6 for the other stop order.

Placing Pending Orders


There is no crucial difference in programming between placing of pending orders and placing of market ones.

You should only note the fact that the assets necessary to modify the pending order into a market one are checked for their sufficiency neither by the client terminal or by the server. They are not limited either. You can place a pending order for the amount that many times exceeds the amount of money available on your account. Such an order can be kept for indefinite periods of time. When the market price reaches the level of the open price requested for the pending order, there will be a check made on the server. If there are enough money on the account for opening this order, it will be modified into a market one (opened). If not, it will be deleted.

Function WindowPriceOnDropped()


In MQL4, we have a very important feature - we can determine programmatically in the symbol window the coordinates of the location, at which an Expert Advisor or a script has been placed, if they have been attached using a mouse. For example, we can obtain the ordinate value of attachment of the script using the function WindowPriceOnDropped().

double WindowPriceOnDropped()

The function returns the value of the price in the point of the chart, in which the EA or the script has been dropped. The value will be true only, if the EA or the script has been moved using a mouse ('drag and drop'). This value is not defined for custom indicators.


An example of a simple script that opens a BuyStop order costing 35% of the free margin, with some preset values of stop orders (openbuystop.mq4).

//------------------------------------------------------------------------------------
// openbuystop.mq4
// The code should be used for educational purpose only.
//------------------------------------------------------------------------------- 1 --
int start() // Special function start
{
int Dist_SL =10; // Preset SL (pt)
int Dist_TP =3; // Preset TP (pt)
double Prots=0.35; // Percentage of free margin
string Symb=Symbol(); // Symbol
double Win_Price=WindowPriceOnDropped(); // The script is dropped here
Alert("The price is set by the mouse as Price = ",Win_Price);// Set by the mouse
//------------------------------------------------------------------------------- 2 --
while(true) // Cycle that opens an order
{
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Min. distance
double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Min. volume
double Free =AccountFreeMargin(); // Free Margin
double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Cost per 1 lot
double Lot=MathFloor(Free*ProtsOne_LotMin_Lot)*Min_Lot;// Lots
//------------------------------------------------------------------------- 3 --
double Price=Win_Price; // The price is set by the mouse
if (NormalizeDouble(Price,Digits)< // If it is less than allowed
NormalizeDouble(Ask+Min_Dist*Point,Digits))
{ // For BuyStop only!
Price=Ask+Min_Dist*Point; // No closer
Alert("Changed the requested price: Price = ",Price);
}
//------------------------------------------------------------------------- 4 --
double SL=Price - Dist_SL*Point; // Requested price of SL
if (Dist_SL < Min_Dist) // If it is less than allowed
{
SL=Price - Min_Dist*Point; // Requested price of SL
Alert(" Increased the distance of SL = ",Min_Dist," pt");
}
//------------------------------------------------------------------------- 5 --
double TP=Price + Dist_TP*Point; // Requested price of TP
if (Dist_TP < Min_Dist) // If it is less than allowed
{
TP=Price + Min_Dist*Point; // Requested price of TP
Alert(" Increased the distance of TP = ",Min_Dist," pt");
}
//------------------------------------------------------------------------- 6 --
Alert("The request was sent to the server. Waiting for reply..");
int ticket=OrderSend(Symb, OP_BUYSTOP, Lot, Price, 0, SL, TP);
//------------------------------------------------------------------------- 7 --
if (ticket>0) // Got it!:)
{
Alert ("Placed order BuyStop ",ticket);
break; // Exit cycle
}
//------------------------------------------------------------------------- 8 --
int Error=GetLastError(); // Failed :(
switch(Error) // Overcomable errors
{
case 129:Alert("Invalid price. Retrying..");
RefreshRates(); // Update data
continue; // At the next iteration
case 135:Alert("The price has changed. Retrying..");
RefreshRates(); // Update data
continue; // At the next iteration
case 146:Alert("Trading subsystem is busy. Retrying..");
Sleep(500); // Simple solution
RefreshRates(); // Update data
continue; // At the next iteration
}
switch(Error) // Critical errors
{
case 2 : Alert("Common error.");
break; // Exit 'switch'
case 5 : Alert("Outdated version of the client terminal.");
break; // Exit 'switch'
case 64: Alert("The account is blocked.");
break; // Exit 'switch'
case 133:Alert("Trading fobidden");
break; // Exit 'switch'
default: Alert("Occurred error ",Error);// Other alternatives
}
break; // Exit cycle
}
//------------------------------------------------------------------------------- 9 --
Alert ("The script has completed its operations -----------------------------");
return; // Exit start()
}
//------------------------------------------------------------------------------- 10 --

The structure of the script openbuystop.mq4 is built in the same way as that of the script openbuy.mq4, so there is no need to describe it in details. We will only turn our attention to basic differences between these programs.

The price, at the level of which the script has been attached to the symbol window, is determined in the line:

   double Win_Price=WindowPriceOnDropped();     // A script is dropped here

Subsequently, the value of this variable is kept unchanged during the entire period of operation of the program. This is necessary, if the script fails opening an order more than. At the same time, the script will every time calculate the requested value of the price close to the location (to the price level) where user attached the script.

It is easy to see that, in the script openbuystop.mq4, there is no check for sufficiency of free margin for opening of an order, but there is a check of the order open price (block 3-4). If the calculated value of the variable Price does not comply with the requirements of placing of a pending Stop order (see Order Characteristics and Rules for Making Trades, Requirements and Limitations in Making Trades), this value will be recalculated.

In the block of error processing, there are some small changes, as well: some errors are not considered, but the codes of some other errors are processed.

Reasonable Limitations


As related to the use of trade functions, we should pay attention to some more general limitations. For example, error 146 occurs only, if several programs that form trade requests work in one symbol window. In our opinion, this practice is allowable, but not reasonable.

It would be much more efficient to create and use one trading program that would consider all special features of trading. If we use only one trading program, it is just impossible to form several trade request simultaneously. Moreover, the entire algorithm could be organized much better in such a program: consider the probability of successful trades and re-allocate money correctly, according to this probability.

For performing of trades, it is more efficient to use a full-scaled Expert Advisor, whereas a script would be better used for one-time calculations or for displaying some useful information on the screen. At the same time, if the trader does not use an Expert Advisor for automated trading, the use of scripts turns out to be more efficient than working with orders using the control panel of the client terminal.