Simple Expert Advisor
This section dwells on the principles of creating a simple trading Expert Advisor.
||Problem 29. Create a trading Expert Advisor.
Before starting to program a trading Expert Advisor, it is necessary to define general principles of a future program. There are no strict program creating rules. However, once having created a program, a programmer usually continues to improve it. To be able to easily understand the program in future, it must be created in accordance with a well-thought and easy-to-understand scheme (it is especially important if a program will be further improved by another programmer). The most convenient program is the one that consists of functional blocks, each of which is responsible for its part of calculations. To create an algorithm of a trading Expert Advisor, let's analyze what an operating program should do.
One of the most important data in the formation of trade orders is the information about orders that already exist in a client terminal. Some of trading strategies allow only one unidirectional order. Generally, if a trading strategy allows, several orders can be open in a terminal at the same time, though their number should be reasonably limited. When using any strategy, trade decisions should be made taking into account the current situation. Before a trade decision is made in a program, it is necessary to know what trading orders have already been opened or placed. First of all a program must contain a block of orders accounting which is among the first to be executed.
During an EA execution trading decisions should be made, the implementation of which leads to the execution of trade operations. Code part responsible for trade orders formation is better written in a separate block. An Expert Advisor can form a trade request to open a new pending or market order, close or modify any of existing orders or perform no actions at all. An EA must also calculate order prices depending on a user's desire.
Trade decisions should be made in a program on the bases of trade criteria. The success of the whole program depends on the correctness of detecting trade criteria in the program. When calculating trade criteria a program can (and must) take into account all information that can be useful. For example, an Expert Advisor can analyze combination of technical indicator values, time of important news releases, current time, values of some price levels, etc. For convenience, the program part responsible for the calculation of trading criteria should be written in a separate block.
A trading Expert Advisor must necessarily contain error processing block. Analyzing errors that may occur in the execution of trade operation allows, on the one side, to repeat a trade request and, on the other hand, to inform a user about a possible conflict situation.
Structure of a Simple Expert Advisor
Below is a structural scheme of a simple Expert Advisor constructed on the basis of several functional blocks, in each block a certain detached part of calculations.
Fig. 109. Structural scheme of a simple Expert Advisor.
On the following EA development stage there is no program code yet. At the same time the algorithm of a program is to a great extent formed. How the EA built on the bases of the offered scheme will operate can be easily understood simply looking on the scheme and orienting upon block names and relations arrays (control passing) between them.
After program start control is passed to the block of preliminary processing. In this block some general parameters can be analyzed. For example, if there are not enough bars in a window (bars necessary for calculating parameters of technical indicators), an EA will not be able to operate adequately. In such a case an EA must terminate operation preliminarily informing a user about it and reporting about the reason of termination. If there are no contraindicatons of a general character, control is passed to order accounting block.
In the block of accounting orders the number and quality of orders existing in a client terminal for a security (to the window of which the EA is attached) is detected. In this block orders of other securities must be eliminated. If a programmed trading strategy requires using only market orders (and does not use pending orders) the fact of presence of pending orders must be detected. If a strategy admits only one market order and there are actually several orders, this fact should also be known. The task of the order accounting block (in this scheme) is in defining whether the current trading situation corresponds with an expected one, i.e. that in which the EA can adequately operate. If the situation corresponds, control must be passed to the next block to continue the EA's operation; if not, the EA's operation must be terminated and this fact must be reported to a user.
If there are no orders in the terminal or the number and quality of existing orders corresponds to what was expected, control is passed to the block of defining trading criteria. In this block all criteria necessary for making trade decisions are calculated, namely criteria for opening, closing and modifying orders. Further control is passed to the block of closing orders.
It is easy to understand why in the offered scheme the block of closing orders is executed earlier than the block of opening orders. It is always more reasonable to process first existing orders (close or modify) and only after that to open new orders. Generally, it is correct to be guided by the desire to have as little orders as possible. During the execution of this block all orders, for which the closing criterion has been activated, must be closed.
After all necessary orders has been closed, control is passed to a block of new orders size calculation. There are a lot of algorithms for calculating an order volume. The simplest of them is using a constant, fixed lot size. It is convenient to use this algorithm in a program for testing strategies. More popular method of defining an order size is setting the number of lots depending on the amount of free margin, for example 30-40% of it. If free margin is not enough, the program terminates its operation having informed a user about the reason.
After the number of lots for opening new orders is defined, control is passed to order opening block. If any of criteria calculated earlier points to the necessity of opening an order of a certain type, a trade request to open an order is formed in this block.
There is also error analyzing block in an Expert Advisor. If any trade operation failed, control (only in this case) is passed to the error processing block. If an error returned by a server or client terminal is not crucial, one more attempt is made to perform a trade operation. If a crucial error is returned (for example, an account is blocked), an EA must terminate its operation. Remember, in MQL4 there is no possibility of program terminating an EA's operation in a security window (as distinct from scripts, see Special Functions). What can be done in a program way is the termination of start(). At a new start of the function start() on a new tick the value of a certain variable-flag prohibiting trading (in this case enabled as a result of a critical error) can be analyzed and control can be passed for the termination of the special function operation; thus formation of new trade request is not permitted. In the offered scheme the flag value is analyzed in the block of preliminary processing.
Market prices are constantly moving. Market state at any moment of time can be conditionally characterized either as a trend - strong unidirectional price change (rise or fall), or as a flat - lateral price movement with weak deviations from a certain average. These market characteristics are conditional, because there are no clear criteria, according to which trend or flat can be identified. For example, long lateral movements with strong deviations that can be traced neither to a flat nor to a trend. Generally it is assumed that the market is mainly in the state of lateral movement and trends usually take place 15-20% of time.
Fig. 110. Flat and trend in the market.
All trading strategies also can be conventionally divided into two main groups. The first group contains flat-oriented strategies. The main idea of such strategies is that after an evident deviation price must return to the previous position, that's why orders are opened in the direction contrary to the last price movement. The second group strategies are trend strategies, when orders are opened in the same direction as the salt price movement. There are more complicated (combined) strategies. Such strategies take into account many different factors that characterize market; as a result trading can be executed both on flat and trend. It is not hard to implement trading according to this or that strategy technically - MQL4 contains all necessary means for it. The main work in the creation of once own strategy consists in the search of trading criteria.
In this example we will try to construct a trend Expert Advisor, i.e. the one that will open orders in the price movement direction. So, we need to find among various technical indicators those that detect a trend beginning. One of the simplest methods of searching trading criteria is based on the analysis of the combination of MAs with different averaging periods. Fig. 111 and Fig. 112 show the position of two different MA (with periods of averaging 11 and 31) on different market parts. Averages with small averaging period (red lines) are closer to a price chart, twisty and movable. Moving averages with larger period of averaging (blue line) are more inert, have larger lag and are situated farther from market prices. Let's pay attention to places where MAs with different averaging periods cross and try to decide, whether the fact of MA crossing can be used as a reading criterion.
Fig. 111. Crossing of MA(11) and MA(31) when price movement direction changes.
In Fig. 111 we see a market part where opening orders in the direction of price movement at MA crossing is justified. In point A the red line crosses the blue one from bottom upwards, after that the market price continues growing for some time. Further reverse MA crossing indicates the price movement direction change. If we open a Buy order at point A and close it at B, we will get profit proportional to difference of A and B prices.
Fig. 112. Crossing of MA(11) and MA(31) when price movement direction changes.
At the same time there are other moments in the market when MA cross, but this does not lead to further considerable price rise or fall (Fig. 112). Orders opened at MA crossing at such moments will lead to losses. If Sell is opened at A and closed at B, such trading will bring losses. The same can be said about a Buy order opened at B and closed at C.
The success of the whole strategy implemented on the basis of MA crossing depends on the number of parts that can be characterized as trend and flat. In flat often MA crossing is a regular event that interferes with any trend strategy. Numerous false signals as a rule lead to losses. That is why this sign - crossing of MAs with different averaging period - can be used for building trading strategies only in combination with other signs proving a trend. In this example (for constructing a simple Expert Advisor) we will have to refuse using this sign.
We will use another sign. Analyzing visually the character of price changes in the market, we can see that a long one-direction price rise or fall often appears as a result of a short strong movement. In other words, if within a short period a strong movement happened, we may expect its continuation in a medium-term period.
Fig. 113. Strong price movement can lead to a trend development.
Fig. 113 shows the market period when a strong movement resulted in the continuation of the price change in the same direction. As the "a strong movement" we may use the difference of MAs with different averaging periods. The stronger the movement, the larger is the lag of MA with larger averaging period from MA with a small period of averaging. Moreover, even strong discontinuous price movements with further return do not result in a large difference between MAs, i.e. numerous false signals do not appear. For example, price jump by 50 points with further return (in the center in Fig. 113) entailed increase of difference between MAs only by 20 points. At the same time a really strong movement (which is not usually accompanied by a considerable correction) in point A resulted in the difference increase up to 25 - 30 points.
If Buy order is opened when a certain value of difference between MAs is reached, for example in A, most probably the order will be profitable when a price reaches a preset Stop order value. Let's use this value as a trading criterion in our Expert Advisor.
Number of Orders
In this example we analyze an Expert Advisor that admits presence of only one market order, pending orders are not provided. Such an approach is justified not only in this certain example, but can be used as the basis for any strategy.
Pending orders are usually used when a developer has quite a reliable criterion for forecasting the future price change with high probability. If there is no such criterion, no need to use pending orders.
The situation when several opposite orders for one security are open also cannot be considered reasonable. It was written earlier that from economical point of view opposite orders are considered to be senseless, especially if the order prices are equal (see Closing and Deleting Orders). In such a case we should close one order by another one and wait for a signal to open one market order in a certain direction.
Relation of Trading Criteria
From this position it becomes clear what relations are possible between trading criteria. Fig. 114 shows three variants of correlation of trading criteria, when each criterion is important (valid). Actions (opening and closing market orders) take place clockwise on the following pictures.
Fig. 114. Order opening and closing criteria correlation (a and b - correct, c - incorrect).
The most popular variant of a correctly formed trading criteria is the variant a. After being opened a market order Buy is held upon till the moment when criterion requiring its closing triggers. After that a pause occurs when no orders are opened. Further a market order Sell can be opened. Conditions for closing a Sell order (in accordance with correctly formed criteria) occur earlier, than conditions for opening a Buy order. However, a Buy order can be opened once again, if a trading criterion requires this. But according to this variant a market order cannot be opened if there is an open market order in the contrary direction.
Similar criteria correlation is in the variant b. The difference is that a criterion for opening any market order is at the same time a criterion for closing the opposite order. This variant like the variant a does not allow several orders opened in the terminal at the same time on one security.
The variant of criteria correlation is incorrect. According to this variant opening of a market order is allowed when contrary orders are not closed yet, which is senseless. There can be rare cases when this variant is partially justified. Opening of an opposite order is sometimes acceptable for compensating losses occurring at small corrections after strong price movements. In such cases an opposite order can be opened of the same or smaller value than the already existing one and then closed when the correction is over. Such a tactic allows not to interfere with the "main" order opened in the trend direction.
In general case several one-direction orders are also possible. This may be justified when an earlier opened order is protected by a Stop order and the criterion pointing at the price development in the same direction triggered once again. However, when creating such a strategy, a developer must be fully aware that in case of a sharp price movement change the placed stop orders may be unexecuted by some brokers at the first price touch. And the loss will be proportionate to the total value of one-directional market orders.
In our example we use variant b of trading criteria correlation. All opened market orders are closed either by a stop order or after a criterion of opening an order in opposite direction triggers (here criterion of closing Buy coincides with that of opening Sell and vice versa).
Size of Opened Orders
In any trading strategy order sizes should be reasonably limited. In a simple case a fixed order size is used in an Expert Advisor. Before EA operation start, a user can set any size of future orders and leave it unchanged for some time. Further if balance changes, a user can set up a new value of lot numbers of opened orders.
A too small order size provides more confidence in operation at the unpredictable market change, but the profit in case of success will be not so large. If the order size is too large, large profit can be acquired, but such an EA will be too risky. Usually the size of opened orders is set up so, that margin requirements do not exceed 2-35% percent of the balance or free margin (if a strategy allows only one opened order, balance and free margin at the moment before the order opening will be equal).
In this example both variants are implemented. A user may choose either to indicate directly values of orders or set the value in percentage from the free margin.
A simple trend Expert Advisor tradingexpert.mq4 constructed on the basis of previous arguments can look like this:
#property copyright "Copyright © Book, 2007"
#property link "http://AutoGraf.dp.ua"
extern double StopLoss =200;
extern double TakeProfit =39;
extern int Period_MA_1=11;
extern int Period_MA_2=31;
extern double Rastvor =28.0;
extern double Lots =0.1;
extern double Prots =0.07;
if(Bars < Period_MA_2)
Alert("Not enough bars in the window. EA doesn't work.");
Alert("Critical error. EA doesn't work.");
for(int i=1; i>=OrdersTotal(); i++)
Alert("Pending order detected. EA doesn't work.");
Alert("Several market orders. EA doesn't work.");
if (MA_1_t > MA_2_t + Rastvor*Point)
if (MA_1_t > MA_2_t - Rastvor*Point)
if (Tip==0 && Cls_B==true)
Alert("Attempt to close Buy ",Ticket,". Waiting for response..");
Alert ("Closed order Buy ",Ticket);
if (Tip==1 && Cls_S==true)
Alert("Attempt to close Sell ",Ticket,". Waiting for response..");
Alert ("Closed order Sell ",Ticket);
if (Lots < 0)
if(Lts > Min_Lot) Lts=Min_Lot;
if (Lts*One_Lot > Free)
Alert(" Not enough money for ", Lts," lots");
if (Total==0 && Opn_B==true)
SL=Bid - New_Stop(StopLoss)*Point;
TP=Bid + New_Stop(TakeProfit)*Point;
Alert("Attempt to open Buy. Waiting for response..");
if (Ticket < 0)
Alert ("Opened order Buy ",Ticket);
if (Total==0 && Opn_S==true)
SL=Ask + New_Stop(StopLoss)*Point;
TP=Ask - New_Stop(TakeProfit)*Point;
Alert("Attempt to open Sell. Waiting for response..");
if (Ticket < 0)
Alert ("Opened order Sell ",Ticket);
int Fun_Error(int Error)
case 4: Alert("Trade server is busy. Trying once again..");
case 135:Alert("Price changed. Trying once again..");
case 136:Alert("No prices. Waiting for a new tick..");
case 137:Alert("Broker is busy. Trying once again..");
case 146:Alert("Trading subsystem is busy. Trying once again..");
case 2: Alert("Common error.");
case 5: Alert("Old terminal version.");
case 64: Alert("Account blocked.");
case 133:Alert("Trading forbidden.");
case 134:Alert("Not enough money to execute operation.");
default: Alert("Error occurred: ",Error);
int New_Stop(int Parametr)
if (Parametr > Min_Dist)
Alert("Increased distance of stop level.");
One more criterion in program estimation is its readability. A program is considered to be correctly written, if it can be easily read by other programmers, that's why all main program parts and main moments characterizing the strategy must be commented. This is also why it is recommended to declare and comment all variables at the beginning of the program.
In the block 1-2 external and global variables are described.
According to rules, external and global variables must be opened before their first usage (see Types of Variables),
that's why they are declared in the program head part. All local variables of the function start() are gathered and described in the upper function part (block 2-3) immediately after the function header. Rules of declaring local variables do not require it, but also do not prohibit. If a programmer faces difficulties in understanding the meaning of a variable when reading the program, he can refer to the upper program part and find out the meaning and type of any variable. It is very convenient in programming practice.
Block of preliminary processing
In this example the preprocessing consists of two parts (block 3-4). The program terminates operation if there are not enough bars in a security window; in such a case it is impossible to detect correctly (in block 5-6) values of moving averages necessary for calculating criteria. Besides here the value of the variable Work is analyzed. In the normal EA operation the variable value is always 'true' (it is set once during initialization). If a critical error occurs in the program operation, 'false' is assigned to this variable and start() finishes its operation. This value will not change in future, that is why the following code is not executed. In such a case the program operation must be stopped and the reason for the critical error must be detected (if needed, a dealing center must be contacted). After the situation is solved, the program can be started once again, i.e. the EA can be attached to a security window.
The described Expert Advisor allows working only with one market order. The task of the orders accounting block (block 4-5) is to define characteristics of an opened order, if there is one. In the loop going through orders 'for' all existing market and pending orders are checked, namely from the first (int i=1) to the last one
(i<=OrdersTotal()). In each cycle iteration the next order is selected by the function OrderSelect(). The selection is made from a source of opened and pending orders (SELECT_BY_POS).
If the selection is executed successfully (i.e. there is one more order in the terminal), further this order and the situation must be analyzed: whether the order is opened for the security, at which the EA operates, whether the order is market or pending; it also must be taken into account when counting orders. In the line:
all orders opened for another security are eliminated. Operator 'continue' stops the iteration and characteristics of such an order are not processed. But if the order is opened for the security, to the window of which the EA is attached, it is further analyzed.
If OrderType() returns value more than 1 (see Types of Trades), the selected order is a pending one. But in this Expert Advisor managing pending orders is not provided. It means the execution of start() must be terminated, because a conflict situation occurred. In such a case after a message about the operation termination start() execution is stopped by the operator 'return'.
If the last check showed that the analyzed order is a market order, the total number of orders for a security is calculated and analyzed. For the first of such orders all necessary characteristics are defined. If in the next iteration the order counter (variable Total) finds the second market order, the situation is also considered to be conflict, because the EA cannot manage more than one market order. In such a case start() execution is stopped after showing a corresponding message.
As a result of the order accounting block execution (if all checks were successful) the variable Total preserves its zero value if there are no market orders, or gets the value 1 if there is a market order for our security. In the latter case some variables set in correspondence with the order characteristics (number, type, opening price, stop levels and order value) also get their values.
Calculating Trading Criteria
In the analyzed example definition of trading criteria (block 5-6) is calculated on the bases of difference between Moving Averages with different periods of averaging. According to accepted criteria a chart is bull-directed if the current value of the MA with smaller period is larger than the value of MA with larger period, and the difference between the values is larger than a certain value. In a bear movement MA with smaller period is lower than MA with larger period and the difference is also larger than a certain critical value.
At the block beginning values of MAs with averaging periods Period_MA_1 and
Period_MA_2 are calculated. The fact of significance of any trading criterion is expressed via the value of a corresponding variable. Variables Opn_B and Opn_S denote the criterion triggering for opening Buy and Sell orders, variables Cls_В and Cls_S - for closing. For example, if a criterion for opening Buy has not triggered, the value of Opn_B
remains 'false' (set at the variable initialization); if it has triggered, Opn_B gets the value 'true'. In this case the criterion for closing Sell coincides with that for opening Buy, criterion for opening Sell coincides with that for closing Buy.
||Trading criteria accepted in this example are used for educational purpose only and must not be considered as a guideline when trading on a real account.
It was written earlier that this Expert Advisor is intended for operation only with one market order opened for a security, to which window the EA is attached. To the moment when control in the program is passed to the order closing block it is known for sure that at the current moment there are either no orders for the security, or there is only one market order. That's why the code in orders closing block is written so that only one order can be closed successfully.
This block is based on the infinite loop 'while', the body of which consists of two analogous parts: one for closing a Buy order, another for closing a Sell order. 'While' is used here for the purpose that in case of a trade operation failure it could be repeated once again.
In the header of the first operator 'if' condition for closing a Buy order is calculated (Sell orders are closed in the analogous way). If the type of an earlier opened order corresponds to Buy (see Types of Trades) and the sign for closing Buy is relevant, control is passed to the body of 'if' operator where a request to close is formed. As an order closing price in the function OrderClose() the value of a two-sided quote corresponding to the order type is indicated (see Requirements and Limitations in Making Trades). If a trade operation is executed successfully, after a message about the order closing is shown the current 'while' iteration is stopped and the execution of the order closing block is over. But if the operation fails, the user-defined function for processing errors Fun_Error() is called (block 10-11).
As a passed parameter in Fun_Error() the last error code calculated by GetLastError() is used. Depending on the error code Fun_Error() returns 1 if the error is not critical and the operation can be repeated, and 0 if the error is critical. Critical errors are divided into two types - those, after which a program execution can be continued (for example, a common error) and those, after which execution of any trade operations must be stopped (for example, blocked account).
if after an unsuccessful trade operation the user-defined function returns 1, the current 'while' iteration is terminated and during the next iteration another attempt is made to execute the operation - to close the order. If the function returns 0, the current start() execution is stopped. On the next tick start() will be started by the client terminal again and if еору conditions for order closing are preserved, another attempt to close the order will be made.
If during error processing it is found out that further program execution is senseless (for example the program operates on an old client terminal version) during the next start the execution of the special function start() will be terminated in the block of preliminary processing when analyzing the value of the variable Work.
Calculating Amount of Lots for New Orders
Amount of lots can be calculated in accordance with a user's settings following one of the two variants. The first variant is a certain constant value set up by a user. According to the second variant the amount of lots is calculated on the basis of a sum equal to a certain percentage (set by a user) of a free margin.
At the beginning of the block of defining the amount of lots for new orders (block 7-8) necessary values of some variables are calculated - minimal allowed amount of lots and step of lot change set up by a broker, free margin and price of one lot for the security.
In this example the following is provided. If a user has set up a certain non-zero value of the external variable Lts, for example 0.5, it is accepted as the amount of lots Lts when a trade request to open an order is formed. If 0 is assigned to Lts, the number of lots Lts is defined on the basis of the variable Prots (percentage), free margin and conditions set up by a broker.
After Lts is calculated, a check is conducted. If this value is lower than the minimal allowed value, the minimal allowed value is accepted. but if free margin is not enough, after a corresponding message the start() execution is terminated.
The block of opening orders (block 8-9) like the bloke of opening orders is an infinite loop 'while'. In the header of the first operator 'if' conditions for opening a Buy order are calculated: if there are no orders for the security (variable Total is equal to 0) and the sign for opening a Buy order is relevant (Opn_B is true ), control is passed to 'if' operator body for opening an order. In such a case after rates are refreshed prices for stop levels are calculated.
Values of stop levels are initially set by a user in external variables StopLoss and TakeProfit. In a general case a user can set values for this parameters smaller that a broker allows. Besides a broker may change the minimal allowed distance at any moment (it is an often case at strong market movements, for example, before important news release). That's why before each order opening stop levels must be calculate taking into account values set bu a user and the minimal allowed value set up by a broker.
For calculating stop levels the user-defined function New_Stop() is used; as a passed parameter the stop level value set by a user is used. In New_Stop() first the current minimal allowed distance is calculated. If the value set by a user corresponds to a broker's requirements, this value is returned. If it is smaller than the allowed value, the value allowed by a broker is used. Prices of stop requests are calculated from the corresponding two-sided quote (see Requirements and Limitations in Making Trades).
A trade request to open an order is formed using the function
OrderSend(). For the calculation of order opening price and prices of stop requests the two-sided quote values corresponding to the order type are used. If a trade operation was successful (i.e. a server returned the number of an opened order) after a message about a successful order opening is shown. start() execution is finished. If an order was not opened and the client terminal returned an error, the error is processed according to the algorithm described earlier.
Some Code Peculiarities
The analyzed Expert Advisor code is oriented to the implementation of a certain strategy. Note, some program lines contain variables and calculations that would be changed, if the strategy were changed.
For example, according to the accepted strategy the Expert Advisor is developed to work only with one order. This allowed to use the variable Ticket both for the identification of a closing order number (in block of closing 6-7) and for the identification of a success of a trade operation execution when opening an order (in the block of opening 8-9). In this case such a solution is acceptable. However, if we take the analyzed code as the basis for the implementation of another strategy (for example allow opposite orders) we will have to introduce one or several variables to be able to recognize numbers of opened orders and identify the success of trade operations.
In further strategy modifications we will have to change come program lines containing part of logics contained in the source strategy. Namely in the order accounting block we will not have to terminate the program operation if there are several open orders for a security. Besides, conditions for opening and closing orders will alslo change. This will entail the code changing in blocks of opening and closing orders.
On the basis of this analysis we can easily conclude that the described simple Expert Advisor is not perfect. In a general case, for the implementation of order accounting one should use a universal function based on using data arrays and not containing logics of a certain strategy. The same can be said about the blocks of opening and closing orders. A more complete program must contain a main analytical function, all other user-defined functions must be subordinate to it. This analytical function must contain a program code, in which all conditions for the implementation of any strategy are analyzed; all subordinate functions must perform limited actions. The function of accounting orders must only account orders, functions of opening and closing orders must only open and close orders, and the analytical function must "think" and manage all other functions, i.e. call them when needed.