The first stage of creating a trading robot

This article is a continuation of the one started by in the 61st issue of ForTrader.org of the series "Creating a trading robot: from A to Z". It is assumed that at this stage the reader is already familiar with the trading system, which we are converting into a robot, so within the framework of this article we will consider the initial phase of writing the Expert Advisor. This version of the robot will be somewhat simplified to make the source code easier to understand.

Logic modules of the robot

For realization trading robot It must first be determined which logic modules it should consist of.

Trading signals detection module

The purpose of this module is to trading instrument searchfor which there is a signal at the given moment of time. The module will determine the signal for each specific trading instrument. Thus, to determine the general trend for a group of instruments, you will need to use this module as many times as the number of instruments provided in the robot settings.

From the program's point of view, this might look like two-dimensional array fillingwhere the zero index of the second dimension is the name of the trading instrument, and the first one is the signal by instrument. That is, to find a common signal (common direction) at any moment of time it will only be necessary to traverse the array, without redefining the signals by instruments. If we refer to the description of the proposed trading system, we can remember that also for each specific instrument will be determined trading lot. In this regard, it makes sense to use the second index in the second dimension of the array, where the lot of this trading instrument will be specified. In this implementation of the robot, this module will not be present (to simplify code perception), so the second index of the second dimension of the array will be filled with the same number - the working lot from the robot settings.

Let's return to the idea of a module for determining the signal by a particular instrument. The module will be a separate subroutine (function). The code of the module itself will be as follows:

//+——————————————————————————————————————-+
//|                                          Trading signals search function |
int find_signal(string Symb) {
if(iOpen(Symb,0,0)>iMA(Symb,0,MA_Period,MA_Shift,MA_Method,MA_Method,MA_Applied_Price,0))
/* if the opening price by Symb symbol on this timeframe is higher than the moving average - up signal*/.
return(1);
if(iOpen(Symb,0,0)<iMA(Symb,0,MA_Period,MA_Shift,MA_Method,MA_Method,MA_Applied_Price,0))
/* if the opening price by Symb symbol on this timeframe is lower than the moving average - down signal*/.
return(-1);

/* if there is no signal, return 0 */
return(0);
}
//|                                          Trading signals search function |
//+——————————————————————————————————————-+
In this case, the parameters of the moving average are placed in the input parameters of the robot (at the top of the robot code, you will see it later):

extern int MA_Period = 8; //moving average averaging period
extern int MA_Shift = 0; //shift moving average
extern int MA_Method = 1; /* moving average calculation method:
0 is a simple moving average,
1 - exponential moving average,
2 - smoothed moving average,
3 - linear weighted moving average
*/
extern int MA_Applied_Price = 1; /* used price for moving average calculation:
0 is the closing price,
1 is the opening price,
2 is the maximum price,
3 is the minimum price,
4 - average price, (high+low)/2,
5 is the typical price, (high+low+close)/3,
6 - weighted closing price, (high+low+close+close+close)/4
*/

Thus, when the function find_signal (string Symb) is called, a signal will be obtained by Symb: 1 - rise, -1 - fall and 0 - no signal.

1. Module for checking the presence of an open order

It is necessary to understand whether there is an open order of this robot on this instrument or not. If there is no deal, the robot will look for an opportunity to open it. If there is one, the robot will follow it and look for an opportunity to close it.

Moving on to the topic of closing the deal (getting ahead of ourselves), it should be noted that mark-to-market is possible in the case when the overall signal changes in the opposite direction (relative to the direction of the open order). Since the trading robot will use both ***USD and USD*** instruments (for example, GBPUSD and USDCHF), it is necessary to introduce some labels - how exactly the robot should evaluate this or that instrument. In the terminology of our team (due to the experience of creating trading robotsThis mark is called "instrument coefficient", and for instruments of *** type it is equal to 1, and for *** -1 (where in our case is USD). I.e. in the example for GBPUSD it will be equal to 1 and for USDCHF it will be equal to -1. These coefficients are taken as input parameters and added to the array of instruments in the robot code as another index of the second dimension. Thus, our target two-dimensional array will also store the label of where this instrument is "looking" relative to the whole group.

Let us return to this module. It will also be implemented as a separate subroutine (function). The code is quite simple:

//+——————————————————————————————————————-+
//|                                           Open order search function |

int find_orders(int type=-1, int magic=-1, string Symb=NULL) {
int i = 0; //counter for the loop

for (i=OrdersTotal()-1; i>=0; i-) {
if (!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
//if no order is selected, go to the next step of the loop

if(((OrderType()==type) || (type==-1))
&& ((OrderMagicNumber()==magic) || (magic==-1))
&& (OrderSymbol()==Symb))) {
/* IF (((order type==type) OR (type==-1))
AND ((magic number of order==magic) OR (magic==-1))
AND (order symbol==Symb))
then return the order ticket */
return(OrderTicket());
break; //exit from the loop
}
}

// if we have reached this place - it means that the order is not found - return -1
return(-1);
}
//|                                           Open order search function |
//+——————————————————————————————————————-+

If an order is detected, the function will return its ticket, otherwise the value is "-1"

2. Transaction opening module

A trading robot must open trades. A separate module (subroutine, function) should be used to open them. Often developers limit themselves to the standard function of opening orders, which is not always convenient. Thus, when using a separate subroutine, you can initially lay down a handler of erroneous situations, make calculations of working levels. StopLoss и TakeProfit etc.

The code of this module includes not only the function of opening orders, but also the calculation of StopLoss and TakeProfit levels, as well as the function that is used to decipher possible error codes (which is often convenient to see error explanations in the journal):

//+——————————————————————————————————————-+
//|                                              Order opening function |
void open_positions(int signal, double lot, int magic, double price=0.0, string symb="NONE") {
//signal=0 -> buy opening signal
//signal=1 -> signal to open a sale
int i = 0; //variable for loop counter
int count = Count_Of_Trade_Try; // number of attempts to open an order in case it cannot be executed at once
int err=0;
if(symb=="NONE") symb=Symbol();
if(signal==0)
price=MarketInfo(symb,MODE_ASK); //open price for purchases
if(signal==1)
price=MarketInfo(symb,MODE_BID); //open price for sales

while(i<=count) {
//the function of order opening itself (built-in). For ease of perception the parameters are separated on different lines:
int ticket = OrderSend(Symbol(), //symbol
signal, //order type
lot, //volume
price, //open price
Slipage, //level of permissible requote
sl(SL,signal,price), // Stop Loss value
tp(TP,signal, price), //Take Profit value
Order_Comment, //order commentary
magic, // magic number
0, //expiration time (used for pending orders)
CLR_NONE); //color of the displayed arrow on the chart (CLR_NONE - the arrow is not drawn)
if(ticket!=-1) //if the opening was successful, apply the graphical object and exit the loop
break;
err=GetLastError();
if(err!=0) Print("Error: "+Market_Err_To_Str(err));
i++;

Sleep(Pause_Of_Trade_Try*100); //in case of an error, pause before a new attempt
} //end while(i<=count)
} //end void open_positions(int signal, double lot, int magic, double price=0.0, string symb="NONE")
//|                                              Order opening function |
//+——————————————————————————————————————-+

//+——————————————————————————————————————-+
//|                                  Function for calculating Stop Loss value for orders |
double sl(int sl_value, int type, string symb="NONE", int rmode=1) {
//type=0 -> market purchases
//type=1 -> market sales
if(symb=="NONE") symb=Symbol();
if(sl_value<=0) return(0);
if(rmode==1) {
if(type==0) return(MarketInfo(symb,MODE_ASK)-sl_value*MarketInfo(symb,MODE_POINT)); //for purchases
if(type==1) return(MarketInfo(symb,MODE_BID)+sl_value*MarketInfo(symb,MODE_POINT)); //for sales
}
if(rmode==2) {
if(type==0) return(MarketInfo(symb,MODE_BID)-sl_value*MarketInfo(symb,MODE_POINT)); //for purchases
if(type==1) return(MarketInfo(symb,MODE_ASK)+sl_value*MarketInfo(symb,MODE_POINT)); //for sales
}
} //end double sl(int sl_value, int type, double price=0.0, string symb="NONE", int rmode=1)
//|                                  Stop Loss calculation function for orders |
//+——————————————————————————————————————-+

//+——————————————————————————————————————-+
//|                                 Function for calculation of Take Profit value for orders |
double tp(int tp_value, int type, string symb="NONE") {
//type=0 -> market purchases
//type=1 -> market sales
if(symb=="NONE") symb=Symbol();
if(tp_value<=0) return(0);
if(type==0) return(MarketInfo(symb,MODE_ASK)+tp_value*MarketInfo(symb,MODE_POINT)); //for purchases
if(type==1) return(MarketInfo(symb,MODE_BID)-tp_value*MarketInfo(symb,MODE_POINT)); //for sales
} //end double tp(int tp_value, int type, double price=0.0, string symb="NONE")
//|                                 function for calculating the Take Profit value for orders |
//+——————————————————————————————————————-+
//+——————————————————————————————————-+
//|                                      Error code decoding function |
string Market_Err_To_Str(int err) {
/* function covers only error codes of trade operations */
switch(err) {
case(0): return("No error");
case(1): return("There is no error, but the result is unknown");
case(2): return("General error");
case(3): return("Incorrect parameters");
case(4): return("Trade server is busy");
case(5): return("Older version of the client terminal");
case(6): return("No connection with the trade server");

case(7): return("Not enough rights");
case(8): return("Too frequent requests");
case(9): return("Invalid operation disrupting server operation");
case(64): return("Account blocked");
case(65): return("Incorrect account number");
case(128): return("The waiting period for the transaction has expired");
case(129): return("Incorrect price");
case(130): return("Wrong feet");
case(131): return("Incorrect volume");
case(132): return("Market closed");
case(133): return("No trading allowed");
case(134): return("Not enough money to complete the transaction");
case(135): return("The price has changed");
case(136): return("No prices");
case(137): return("Broker is busy");
case(138): return("New Prices");
case(139): return("The order has been blocked and is already being processed");
case(140): return("Only purchase allowed");
case(141): return("Too many requests");
case(145): return("Modification is prohibited because the order is too close to the market");
case(146): return("The trading subsystem is busy");
case(147): return("The use of the expiration date is prohibited by the broker");
case(148): return("The number of open and pending orders has reached the limit set by the broker");
case(149): return("Attempt to open an opposite position to an existing position in case hedging is prohibited");
case(150): return("Attempt to close a position on an instrument in contradiction with the FIFO rule");

default: return("");
} //end switch(err)
} //end string Err_To_Str(int err)
//|                                      Error code decoding function |
//+——————————————————————————————————-+

3. order closing module

The robot should close the trade in case the general trend changes to the opposite one. Since the function of searching for an open order returns the order's ticket, it will be reasonable to close the order directly by its ticket within the framework of this module. The code of such a module is also very simple and will be quite understandable even for beginners in MQL4:

//+——————————————————————————————————————-+
//|                                  Function of order closing by its number (ticket) |
bool close_by_ticket(int c_ticket, int slipage) {
/*
function of closing a deal by its number (ticket).
When closing a market order, the level of maximum allowable slipage (slipage) is taken into account
*/

int i = 0, //variable for loop counter
err = 0;
bool ticket = false; // the variable to indicate (non)success of the fact of closing a deal
double price = 0.0; //price for closed deal (for market orders)
if(OrderSelect(c_ticket,SELECT_BY_TICKET,MODE_TRADES)) { //select an order by ticket
if(OrderType()==OP_BUY) price = Bid; //price for purchases
if(OrderType()==OP_SELL) price = Ask; //price for sales
for(i=0;i<=Count_Of_Trade_Try;i++) {
if(OrderType()<=1) // if it is a market order - close it, if it is a pending order - delete it
ticket=OrderClose(OrderTicket(),OrderLots(),price,slipage,CLR_NONE); //close market order
else
ticket=OrderDelete(OrderTicket()); //delete pending order

if(ticket) { // if closing or deleting was successful - return true and exit the loop
return(true);
break;
} //end if(ticket)
err=GetLastError();

if(err!=0) Print("Error: "+Market_Err_To_Str(err));
Sleep(Pause_Of_Trade_Try*100); //in case of an error, pause before a new attempt
} //end for(i=0;i<=Count_Of_Trade_Try;i++)
} //end if(OrderSelect(c_ticket,SELECT_BY_TICKET,MODE_TRADES))

return(false); //return false (in case the order could not be closed or deleted).
} //end bool close_by_ticket(int c_ticket)
//|                                 Function of order closing by its number (ticket) |
//+——————————————————————————————————————-+

4. Final robot code

For simplicity of perception, we did not add the money management module to this version of the robot (see the previous article), it will appear in the next article. Also, this version of the robot does not include trailing stop loss functionwhich will also be included in the code of the next article.

Now it's time to assemble the resulting modules into a single whole and add the missing links between them. All this will be done in the predefined language function start().
The code itself is presented below, it is provided with comments and should not be difficult to understand.

You can download and analyze the source code of the resulting Expert Advisor with a detailed description and missing functional parts here.

5. Testing the robot

The robot will be tested on H4 tamframe. The parameters that are entered by default are used. If changes are made, this will be reported separately.

Leave a Reply

Back to top button