Automated Choice of Brokerage Company for an Efficient Operation of Expert Advisors

Shashev Sergei | 10 October, 2007

Introduction

Very often we face situations when an Expert Advisor successfully operates with one brokerage company and is not profitable or even lossmaking on the other one. The reasons may be different. Different brokerage companies have different settings:

  • Quotes. They slightly differ because of two factors - different data feeds and different filtration that smooths quotes. For some Expert Advisors this may be relevant. Situations may occur when an EA trades often with one brokerage company and rarely on another one.
  • Slippage. It may differ much in different brokerage companies. This also may lead to worse characteristics of an EA because of lower expected profit.
  • Requotes. In some brokerage companies they are oftener than in others. In this case an EA will miss successful entering points because of large number of requotes.
So, the operation of some EAs depends greatly on the brokerage company it works with. If there were no choice, we could put up with quotes, slippage, and requotes. But now we have choice, and it is getting wider and wider. So, we can compare different brokerage companies, see their technical characteristics, and using this information we can choose the best brokerage companies for our EA.

Statistic

Of course we can start several terminals from different brokerage companies, use them a month or two and then choose one, with which the profit is maximal. But such testing is low-informative. It would be better to get more information: mean slippage per a trade, number of requotes that take place at opening of each certain trade, opening time and so on. In order not to have to analyze logs, a project Statistic was developed. It is based on the following set of rules:

  1. An EA analyzes the market, makes transactions, gets all necessary data about the trade and passes it into a common module.
  2. This module contains all information about the current and closed deals. It also counts the statistic about all technical characteristics of the brokerage companies.
  3. It should be maximally comfortable for operation with large amounts of data, so that we could see only necessary information, and not all that can be collected and counted.

Analyzing the statistic (number of requotes, time of a trade execution, slippage) and viewing all trades we can conclude, with what brokerage company it is better to work. If statistic on all companies is negative, we should change some EA parameters, for example, time in the market, frequency of trades. And change them until the EA starts working with a profit. If on a certain stage an EA stops bringing profit with one brokerage company being profitable on others, we stop testing this company.



Theory

We can organize submission of data from an EA into an application ether through files, or through dll. The variant with files is easier in terms of technical realization, because it does not require serious system programming. However, it is not very convenient to work with files, because we do not know beforehand where MetaTrader 4 terminals for different brokerage companies will be, on what currencies they will be tested, what to do if the files are lost, and so on. If we need everything to be done dynamically, with maximum security, it is better to organize data passing through dll.


For different terminals to operate with one dll, it should be located in the system directory windows\system32. It is important that EAs from one terminal download one and the same dll copy, because all they operate within one process, which is the terminal (terminal.exe), it means the have one and the same address space, i.e. they operate with the same variables. One terminal with EAs has its own dll copy for all EAs and the same variables, announced inside dll, another terminal has another copy with other variables. Thus a terminal does not get access to variables from another terminal.


We want to create a single, where data from different terminals will be collected. There are different ways to organize a synchronous operation of different processes with one data field. This may be implemented through files, but again we will face the problem of path indicating, as well as the problem with the processing speed if we have many terminals and EAs. The best solution is a dividable core memory. Working with it requires higher attention and knowledge of the features of the used operating system (in our case it is Windows), however, the available possibilities are infinite. For the organization of a successive access to a certain block of the shared memory a special mechanism of program semaphores is used.


Theory conclusion: through dll EAs write data into shared memory, and then in the application, let us call it Monitor, it reads data from the memory, displays it and conducts necessary statistic calculations. When MetaTrader 4 calls DLL for the first time, operating system generates a copy of this DLL for each terminal, because each terminal is a separate process. The operating scheme is in the picture below.


Practice

Expert Advisor

Certainly, data about the current trade should be formed by an Expert Advisor. For data passing we need to form the interface of the function dll. For the implemented task we need three functions:

bool NewExpert(string  isBrokerName, string isInstrument, int Digit);

Create a new Expert Advisor, identifying it by a broker's name and security. For the calculation of some statistical characteristics we pass the number of figures after a point in a security price.

bool NewDeal(string isInstrument, int Action, int magik, 
double PriceOpen, int Slippage, int TimeForOpen, int Requotes);
The registration of a new trade is performed the following way. While the terminal-process is already identified by a broker's name, for a new trade the name of a security upon which the trade is executed is enough. Other parameters are the trade characteristics.

Table 1. Trade Opening

Parameter

Value

Action

0 – buy, 1 - sell

magik

Magic number

PriceOpen

Opening price

Slippage

Slippage

TimeForOpen

Opening duration

Requotes

Number of received requotes


bool CloseDeal(string isInstrument, int magik, double PriceClose, 
               int Slippage, int TimeForClose, int Requotes);

A trade closing is identified upon a security and the magic. Passed parameters:

Table 2. Trade Closing

Parameter

Value

PriceClose

Closing price

Slippage

Slippage

TimeForClose

Closing duration

Requotes

Number of received requotes


Due to this interface, initialization and opening and closing functions will look like this:


Initialization:

int init()
  {
   int Digit;
   if(IsDllsAllowed() == false)
     {
       Print("Calling from libraries (DLL) is impossible." + 
             " EA cannot be executed.");
       return(0);
     }
   if(!IsTradeAllowed())
     {
       Print("Trade is not permitted!");
       return(0);     
     }
   Digit = MarketInfo(Symbol(), MODE_DIGITS);
   if((Digit > 0) && (Bid > 0))
     {  
       if(!NewExpert(AccountServer(), Symbol(), Digit))
         {
           Print("Creation of a new broker failed");
           return (0);
         }                      
       Print("A broker is successfully created ");                    
       return(0);      
     }   
   Print("No symbol in MarketInfo!");       
   return(0);  
  }

During the initialization after checking the terminal parameters (trade permission and confirmation of DLL calling) we receive the information about a security's digits and its current price. If both parameters are more than zero, the security is adequately presented in the terminal and we can work with it. Each broker differs in its name that can be received using the function AccountServer(), upon this name terminals differ from one another in the shared memory. EAs differ in the name of security they are trading with. That is why if different EAs are attached to one and the same currency pair, they will download one and the same DLL copy which may lead to collision.

Function of opening a new order:

int Deal(int act, double Lot)
  {
   int N = 0;
   int ticket;
   int err;
   double Price_open;
   double Real_price;
   datetime begin_deal;
   double Lots;
   int cmd;
   int magik;
   magik = GenericMagik() + 1;   
   Lots = NormalizeDouble(Lot, 1);
// checking margin for a position opening
   AccountFreeMarginCheck(Symbol(), cmd, Lots);
   err = GetLastError();
   if(err > 0)
     {
       Print("No money for new position");
       return(0);
     }      
   begin_deal=TimeCurrent(); 
   while(N < count)
     {
       if(act == 1)
         {
           Price_open = NormalizeDouble(Ask, Digits);
           cmd = OP_BUY;
         }
       if(act == 2)
         {
           Price_open = NormalizeDouble(Bid, Digits);
           cmd = OP_SELL;
         }
       ticket = OrderSend(Symbol(), cmd, Lots, Price_open,
                          slippage, 0, 0, 0, magik);
       if(ticket > 0)
         {
           if(OrderSelect(ticket, SELECT_BY_TICKET) == true)
             {
               Real_price = OrderOpenPrice();
               NewDeal(Symbol(), cmd,magik, Real_price ,
                       MathAbs(Real_price - Price_open),
                       (TimeCurrent() - begin_deal), N);
             }                   
           return(ticket);
         }
       N++;
       Sleep(5000);
       RefreshRates();
     } 
   return(0);
  }

An order is opened by the function Deal with two parameters: action (1 - buy, 2 - sell ) and lot. Each order differs from the previous one in magic - it is incremented. A position tries to open in count attempts. Thу information about the number of attempts together with opening duration, price and slippage is passed into a shared memory, from where it is read by the monitor.


Order closing function:

bool CloseOrder(int magik)
  {
   int ticket, i;
   double Price_close;
   int count = 0;
   datetime begin_time;
   double Real_close; 
   begin_time = TimeCurrent();    
   for(i = OrdersTotal() - 1; i >= 0; i--) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
           if(OrderSymbol() == Symbol()) 
               if(OrderMagicNumber() == magik)
                 {                  
                   while(count < 10)
                     {                       
                       if(OrderType() == OP_BUY)        
                           Price_close = NormalizeDouble(Bid, Digits);
                       if(OrderType() == OP_SELL)        
                           Price_close = NormalizeDouble(Ask, Digits);
                       if(OrderClose(OrderTicket(), OrderLots(),
                                     Price_close, slippage))
                         { 
                           Real_close = OrderClosePrice();
                           CloseDeal(Symbol(), magik, Real_close,
                                     MathAbs(Real_close - Price_close),
                                     (TimeCurrent() - begin_time), count); 
                           return(true);
                         }
                       count++;
                       Sleep(5000);
                       RefreshRates();
                     }
                 }
     }
   return(false); 
  }

Function of closing CloseOrder() has only one input parameter - the magic. An order tries to close several times and this number of attempts will be passed together with the time of transaction execution, closing price and slippage into the memory and then read by the monitor.

The remaining code is the tested EA. So for using Statistic in your own EAs, you need to import the necessary dll functions; for initialization and opening/closing positions use functions Deal and CloseOrder. If you want, you may rewrite these functions, but data on transactions should be passed in accordance with the interface contained in dll.

Below is the example of the implementation of such an EA using DLL (code of the above enumerated functions is not included).
// Enable dll for operation with monitor
#import "statistik.dll"
  bool NewExpert(string  isBrokerName, string isInstrument, 
                 int Digit);   // Create a broker
  bool NewDeal(string isInstrument, int Action, int magik, 
               double PriceOpen, int Slippage, int TimeForOpen,
               int Requotes);
  bool CloseDeal(string isInstrument, int magik, double PriceClose, 
                 int Slippage, int TimeForClose,
                 int Requotes);
#import
//---- 
extern int Num_Deals = 3;
extern int TimeInMarket = 4;
// maximally acceptable slippage
int  slippage = 10;
// time for rest after a trade
int TimeForSleep = 10;
// period of request
int time_for_action = 1;
// number of attempts for opening a position
int count = 5;
// Function of a new bar
bool isNewBar()
  {
    static datetime BarTime;
    bool res = false; 
    if(BarTime != Time[0]) 
      {
        BarTime = Time[0];  
        res = true;
      } 
   return(res);
  }
//+------------------------------------------------------------------+
//| Generation of magic                                              |
//+------------------------------------------------------------------+ 
int GenericMagic()
  {
   int deals;
//----  
   for(int i = OrdersTotal() - 1; i >= 0; i--) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
           if(OrderSymbol() == Symbol())
               if(OrderMagicNumber() != 0)
                   deals++;
     }       
   return (deals);
  }
//+------------------------------------------------------------------+
//| forming signals to open/close a position                         |
//+------------------------------------------------------------------+
int GetAction(int &action, double &lot, int &magic)
   {
    int cnt, total;  
    if(OrdersTotal() <= Num_Deals)
      {
        if(Close[1] > Close[2])
          {
            action = 1;
            lot = 1;
            return(0);
          }
        if(Close[2] < Close[1])
          {
            action = 2;
            lot = 1;         
            return(0);               
          }
      }
    total = OrdersTotal();
    for(cnt = total - 1; cnt >= 0; cnt--)
      {
        if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES))
            if(OrderSymbol() == Symbol())  
                if((TimeCurrent() - OrderOpenTime()) > TimeInMarket*60)
                  {
                    action = 3;
                    magic = OrderMagicNumber();
                    return(0); 
                  }
      }
   }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   int action = 0;
   double lot = 1;
   int magic = 0;     
   while(!IsStopped())
     {
       Sleep(time_for_action*1000);      
       RefreshRates();
       if(isNewBar())
         {
           GetAction(action, lot, magic);
           if(((action == 1) || (action == 2)))
             {                                        
               if(IsTradeAllowed())
                   Deal(action, lot);
               Sleep(TimeForSleep*1000);
             }
           if(action == 3)
             {
               if(IsTradeAllowed())
                   if(!CloseOrder(magik))
                     {
                       Print("MANUAL CLOSING OF A POSITION IS NEEDED");
                       Sleep(TimeForSleep*1000);   
                     } 
             }
           action = 0;
           lot = 0;
           magik = 0;
         }
     }
   Print("A serious error occurred, the EA stopped operating");  
   return(0);
  }
//+------------------------------------------------------------------+
The EA's executive block is an infinite cycle on the start function. At a preset frequency time_for_action the EA calls the analytical function GetAction() , which by reference returns an action that should be done by the EA, lot with which a position should be opened, and magic in case if you need to close a position.

The analytical block is elementary here - buy, if the previous bar was more than the one before it, and sell if vice versa. Positions are closed by time. For testing your own EAs simply rewrite this block in accordance with their algorithm. You may make no changes in the executive part.

DLL

DLL may be implemented in different environments and in different languages. The dll necessary for our work was created in Visual C++. The trades will have the following structure:

struct DealRec
  {
    int Index;
    int Magic;
    int Cur;
    int Broker;
    double PriceOpen;
    double PriceClose;
    int SlipOpen;
    int SlipClose;
    int Action;  // 0 = BUY 1 = SELL
    int TimeForOpen;
    int TimeForClose;
    int ReqOpen;
    int ReqClose;
    int Profit;
    bool Checked; // indication that the position is closed
  };


They will be fulfilled in two stages - at opening and closing. I.e. one part of data (opening price, slippage at opening etc.) is passed at opening, another part (closing price, closing time etc.) is passed at closing. Prototypes of calling a function in dll

__declspec(dllexport) bool __stdcall NewExpert (char *isBrokerName, 
                                                char *isInstrument, 
                                                int Digit);
__declspec(dllexport) bool __stdcall NewDeal (char *isInstrument, 
                                              int Action, 
                                              int magic, 
                                              double PriceOpen, 
                                              int Slippage, 
                                              int TimeForOpen, 
                                              int Requotes);
__declspec(dllexport) bool __stdcall CloseDeal (char *isInstrument, 
                                                int magic, 
                                                double PriceClose, 
                                                int Slippage, 
                                                int TimeForClose, 
                                                int Requotes);


differ from prototypes in MQL4 only in passing lines. You may look through source dll, which may help in creation of other projects. For the project recompiling open the file statistic.dsw using the program Visual C++. The full code dll is in the files statistic.cpp and statistic.h, the remaining ones are subsidiary. All the enumerated files are in Statistic.zip.

Monitor

An optimal tool for a quick writing of applications with tables and graphical interface - solution from Borland. That is Delphi and C++Builder.

Monitor functions: create a shared memory, read data from it and display in tables, keep the statistics of slippage. There are some more options that make the work more convenient. So this is the functional of the monitor:

  1. Keeping the journal of opened positions;
  2. Keeping the journal of closed positions;
  3. Statistics of slippage and requotes;
  4. Adjustable tables;
  5. Saving trades in html-file.


The implementation is in the attached zip-file Statistic Monitor.zip. For the project recompiling use the program C++Builder. Extension of the project file is *.bpr. The main code is in в main.cpp.

Testing

For testing a special EA was created - it has the simplest conditions of entering and closing positions by time (the implementation was shown earlier). The EA with dll and monitor.exe is in the zip-file monitor+dll+expert.zip. When starting, click START, thus creating a shared memory. DLL should be in the folder system32. After that start several terminals and attach the EA to charts of currencies, on which it is going to trade. After a number of trades statistics is accumulated. The data is collected in the monitor/Journal. From time to time they should be transfered into a file, stored in the form of html-page.


The real operation of the application will be the same. It allows traders to compare the operation of different brokerage companies in terms of their technical characteristics and choose the best ones for automated trading.

Conclusion

Enabling dll in MQL4 allows to develop different application programs, which help not only make decisions about trading, but also collect statistics. The latter one may be very useful in trafing and in choosing a brokerage company. The created application should help developers in this difficult search. For analyzing brokers, attach statistic.dll to an Expert Advisor as described in the example analyzed in this article. The files necessary for the work are in monitor+dll+expert.zip. For the operation copy statistic.dll into the folder system32, start Statistic. exe from any location and open terminal with Expert Advisors, which download dll, start trading and passing their data into the shared memory. Statistic.exe creates auxiliary files, that is why it is better to start the application from an empty folder. If the program is interesting to the developers of trading robots, it can be modified and amended.

It should be noted that not all brokerage companies provide similar conditions for the automated trading:

  1. A broker may prohibit automated trading.
  2. A broker may prohibit to indicate at order placing SL or TP https://www.mql5.com/ru/forum/103341.
  3. Nonsymmetrical levels for SL and TP.
  4. May have no option of a mutual opening of orders.
  5. Restriction on the number of simultaneously opened positions in an account. If the number of orders (open positions + pending orders) exceeds the restriction, the function OrderSend will return the error code ERR_TRADE_TOO_MANY_ORDERS.
  6. Other restrictions.

That is why it is strongly recommended to read carefully the regulations of the brokerage company you are going to work with.


The project Statistic shows, what complexes can be created if you add the possibilities of other languages and programming environments to MQL4 options. The created program will be useful for Expert Advisors working with different brokerage companies, because it helps to analyze their technical characteristics in a convenient form. If a brokerage companies has slippages, trades are executed by time and requotes are quite often, then what for do we need such a brokerage company? There are so many alternatives!