Less code, more action... writing an EA

 

I will try (or we will try, if anyone is interested) to make a framework for Expert Advisors. As suitable as possible just for simple things and that do not require any substantial knowledge from the applied programmer.

In contrast to the locally accepted practice, the design will be conducted from top to bottom. In other words, first we write the main (and the only) file of the EA for the end user, remove everything obviously unnecessary, and write a library to make this work. Then we add a new use-case and upgrade the library.

As a counter-thesis and polemic with https://www.mql5.com/ru/articles/5654 and Mr. Karputov's advisors

of course, before proceeding at all, it is necessary to set the "party line" and "target-communism" :

- 100 lines is enough for the user to implement the strategy. (apart from comments, input and other #property).

- The number of new "entities" (functions/classes/methods/constants) should be reduced to a minimum.

- The library must contain a countable number of files.

- Potentially suitable for GUI

- expandable by plugins

Goals achievable ? hard, but in principle YES. There are some ways, some ideas. But there is no ready solution yet :-)

The current step is to organize the acquisition of data by the expert. In such a way that it is easy for the user to describe them, but at the same time there are still "leads" for metadata and further extensions. (going forward - to implement the GUI part of the code, at least the debugging part)


As the initiator, I have created a simple use-case - we trade on crossing of two MA

Correspondingly, the input part looks like this

/** ------- ПАРАМЕТРЫ СОВЕТНИКА ------
**/
input ENUM_APPLIED_PRICE FAST_MA_PRICE=PRICE_CLOSE;
input ENUM_MA_METHOD FAST_MA_METHOD=MODE_EMA;
input int FAST_MA_PERIOD=14;
input int FAST_MA_SHIFT=0;

input ENUM_APPLIED_PRICE SLOW_MA_PRICE=PRICE_CLOSE;
input ENUM_MA_METHOD SLOW_MA_METHOD=MODE_SMA;
input int SLOW_MA_PERIOD=54;
input int SLOW_MA_SHIFT=0;

The next thing a user has to do - to describe what data he receives from input. List them at least:

// просто перечисляем идентификаторы данных
// всех которые нужны для принятия решений
enum ENUM_SERIES {
   FAST_MA,       // id. значений FAST_MA
   SLOW_MA,       // id. значений SLOW_MA
   TOTAL_SERIES   // последний элемент перечисления = кол-во элементов
};

and explain how it calculates/receives this data (it is long, but applies to both terminals).

/// вычисление данных
/// эксперт будет обращаться к функции каждый раз когда ему необходимы данные
/// управление кешированием и очерёдность(взаимозависимость) вычислений лежит на верхнем уровне
/// @arg ea - эксперт
/// @arg id - ид.серии данных
/// @arg shift - сдвиг в серии
/// @arg data[] - кешированные результаты
/// @return double - конкретное значение для [id][shift] или EMPTY_VALUE если не может быть вычилено
/// если данные могут быть кешированы, они должны быть сохраненны в массиве data
double GetData(EA *ea,int id,int shift,double &data[])
{
#ifdef __MQL4__
   // для 4-ки всё просто - по идентификаторам серии и бара получить данные
   switch ((ENUM_SERIES)id) {
      case FAST_MA:
         return data[shift]=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
      case SLOW_MA:
         return data[shift]=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);
   }
   return EMPTY_VALUE;
#else
   // для 5-ки несколко сложнее (и кстати не проверено) - надо ещё заводить хендлы стандартных индикаторов
   // и проводить (возможно)лишнее копирование
   static d_fast_ma=0;
   static d_slow_ma=0;
   if (d_fast_ma==0) d_fast_ma=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
   if (d_slow_ma==0) d_slow_ma=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);  
   double tmp[1];
   switch((ENUM_SERIES)id) {
      case FAST_MA: CopyBuffer(d_fast_ma,0,shift,1,tmp); return data[shift]=tmp[0];
      case SLOW_MA: CopyBuffer(d_slow_ma,0,shift,1,tmp); return data[shift]=tmp[0];
   }
   return EMPTY_VALUE;
#endif
}

and finally describe the trade signal:

/// генерация(вычисление) сигнала при открытии бара
/// @arg ea - эксперт
/// @arg shift - номер бара в таймсерии. Типично будет 0
/// @return int - сигнал OP_BUY или OP_SELL или -1 если сигнала нет 
int SignalOfCross(EA *ea,int shift)
{
   if (FAST_MA_PRICE!=PRICE_OPEN || SLOW_MA_PRICE!=PRICE_OPEN) shift++;
   if (ea.CrossedUp(FAST_MA,SLOW_MA,shift)) {
      return OP_BUY;
   }
   if (ea.CrossedDn(FAST_MA,SLOW_MA,shift)) {
      return OP_SELL;
   }
   return -1;
}

THAT'S BASICALLY ALL. This is enough to implement an EA and it obviously doesn't require the user to read tons of documentation. The user only needs a basic knowledge of MQL. All the rest should be done by the library (or here, the fancy word - engine). It should be built for the user, not for the user to learn another multi-volume API.

By the way, here's OnInit :

int OnInit()
{
   ea = new EA();
   // настраиваем таймфрейм "по умолчанию"
   //   символ и период - текущие
   //   TOTAL_SERIES наборов данных и кешируем по 30 в каждом
   //   для получения данных служит GetData
   ea.SetupTimeframe(_Symbol,_Period,TOTAL_SERIES,30,GetData);
   // настраиваем сигнал "по умолчанию"
   ea.SetupSignal(SignalOfCross);
   // ------ настройки завершены ------
   // остальная часть одинакова для всех советников
   int ret;
   if ((ret=ea.OnInit())!=INIT_SUCCEEDED) {
      return ret;
   }
   EventSetTimer(60);

   return(INIT_SUCCEEDED);
}
  

..

Библиотека для простого и быстрого создания программ для MetaTrader (Часть I). Концепция, организация данных, первые результаты
Библиотека для простого и быстрого создания программ для MetaTrader (Часть I). Концепция, организация данных, первые результаты
  • www.mql5.com
Разбирая огромное количество торговых стратегий, множество заказов на изготовление программ для терминалов MT5 и MT4, просматривая огромное разнообразие различных сайтов по тематике скриптов, индикаторов и роботов для MetaTrader, я пришёл к выводу, что всё это многообразие в подавляющем своём большинстве строится на фактически одних и тех же...
 
Please insert all your codes correctly: it's impossible to look at this greyish dullness. There is a clear plan of action: click on the button in the editor, insert the code in the resulting field. But you stubbornly insert a canvas of text and then try to apply the "code" style to this canvas
 
Vladimir Karputov:
Please insert all your codes correctly: it's impossible to look at this grey gloom. There is a clear plan of action: click on the button in the editor, insert the code in the resulting field. You stubbornly insert a canvas of text and then try to apply the "code" style to this canvas

It (the code) is not inserted correctly. One fragment can still be dealt with, but a bit more is already a pain...

I mean, it's not up to me - it's up to the webmasters. How, in 2019, with the abundance of funds, they have achieved this - a mystery :-)

But to make it more fun in the future, I will write more or less large posts in the wiki first, and then copy-paste them here and reduce the volume of the published code.

 
Maxim Kuznetsov:

It (the code) is not inserted correctly there. One fragment can still be handled, but a bit more is already a pain...

I mean, it's not up to me - it's up to the webmasters. How, in 2019, with the abundance of funds, they have achieved it - it's a mystery :-)

But to have more fun in the future, more or less big posts will be written to the wiki first, and then transferred here by copy-paste and reduce the volume of the published code.

Apparently you have to copy the block of code now and paste it into Notepad

Then copy from Notepad and paste back, but before that create a new block for the "new" code

 

I created a basic template for the EA and made the strategy file separately.

Do you think it's easy for the user?

You still need a minimum knowledge of programming!

And no "help" instructions, videos, nothing saves you.

The user will then need you to pedal strategies for free.

And they will not read the help.

 
Vitaly Muzichenko:

Apparently you should now copy the block of code and paste it into Notepad

Then copy from notepad and paste back, but before that create a new block for the "new" code

Took a bit of trouble, but sort of "coloured" it. :-)

A pebble (or rather a pebble) to the above webmasters: when pasting in the built-in editor, or rather obviously at the first "coloring", the editor ejects some part of the code it does not like. In particular, it arbitrarily edited "if ((ret=ea.OnInit())!=INIT_SECEED) {..}" . Apparently, the highlight algorithm believes that OnInit is the only one and it cannot be overloaded in the class.

 
Is this pseudocode or is it already for application? TheGetData function cannot use "statics" for indicator "handles"(d_fast_ma,d_slow_ma), because the user will take another couple of "handles" for filtering or something else with different parameters (on another symbol, for example). One should either cache "handles" in some object or (if efficiency is not looked at) get "handle" every time again - they should be cached by terminal itself when parameters are equal.
 
Maxim Kuznetsov:

of course, before proceeding at all, it is necessary to set "party line" and "target-communism" :

1) the user only needs 100 lines to implement the strategy. (apart from comments, input and other #property).

2) The number of new "entities" (functions/classes/methods/constants) for it should be minimized.

3) library must contain a countable number of files.

4) potentially suitable for GUI

5) expandable by plugins


3) What difference does it make how many files a library contains? 1, 10, 100, 1000? Everything should be made for the convenience of the developer. If he is comfortable placing everything in small files, feel free. If he/she is used to coding everything in one file, feel free. By and large it's not difficult to assemble a mega-file from a bunch of disparate automated means. So I wouldn't insist on this point.

4) GUI usable - it's not quite clear how it is. The library is an isolated layer of business logic in a mature way. This layer should not depend on the external GUI in any way. It's GUI should depend on this layer.

 
Maxim Kuznetsov:
if (d_fast_ma==0) d_fast_ma=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
if (d_slow_ma==0) d_slow_ma=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);  


Your approach is purely procedural from the outset: what I see is what I tell. What if the average has to be calculated not on prices, but, say, on volume, or another indicator? Do we have to rewrite the user again? The calculation of the average is an algorithm. What you want to apply the algorithm to is the data. You're mixing the algorithm with the data, instead of separating it right away (like this pseudo code):

int period_ma = 12;
int shift = 3;
double fast_ma = SMA(Close, period, shift);

Oh, I forgot the series index. - There really shouldn't be one. You have to compute everything in a ring buffer by combining algorithms into pipelines.

 
Maxim Kuznetsov:

As a counter-thesis and polemic to https://www.mql5.com/ru/articles/5654 and Mr Karputov's advisers

Mine has been undeservedly forgotten. And in vain, there is a lot there.

 
Vasiliy Sokolov:

Your approach is purely procedural from the outset: what I see is what I tell. What if the average has to be calculated not on prices, but, say, on volume, or another indicator? Do we have to rewrite the user again? The calculation of the average is an algorithm. What you want to apply the algorithm to is the data. You're mixing the algorithm with the data, instead of separating it right away (like this pseudo code):

Oops, I forgot the series index. - There really shouldn't be one. You have to compute everything in a ring buffer by combining algorithms into pipelines.

A large part of everything is done for the sake of that. So that you can calculate everything and the Expert Advisor can figure out the sequences and interrelations itself. I once made a spreadsheet calculator a la micro-excel on MT4, and the organization of calculations is based on this model.

The use-case style is fundamentally procedural, as it is the most common. Potential users(novice programmers) write in this way.