Меньше кода, больше прока.. пишем советник

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
Maxim Kuznetsov
19196
Maxim Kuznetsov  

Попробую (или попробуем, если будут заинтересованные) сделать основу для советников. Максимально пригодную именно для простых вещей и не требующую от прикладного программиста существенных знаний.

В отличии от местно-принятой практики, проектирование будет вестись сверху-вниз. От желаемых use-case пользователя, а не от инженерных основ терминала.  То есть сначала пишем основной(и единственный) для конечного пользователя файл советника, убираем оттуда всё явно лишнее, и для того чтобы ЭТО заработало пишем библиотеку.  Затем добавляем новый use-case и модернизируем библиотеку.

на правах контр-тезиса и полемики с https://www.mql5.com/ru/articles/5654 и советниками г-на Карпутова

конечно, перед тем как вообще приступать надо установить "линию партии" и "цель-коммунизм" :

- пользователю для реализации стратегии достаточно 100 строк. (помимо комментариев, input и прочих #property).

- при этом число новых для него "сущностей" (функций/классов/методов/констант) должно сводится к минимуму.

- библиотека должна содержать счётное кол-во файлов.

- потенциально пригодна для GUI

- расширяется за счёт плагинов

Цели достижимы ? тяжело, но в принципе ДА. По некоторым путям есть наработки и идеи. Но готового решения пока нет :-)

Текущий шажок - организовать получение данных экпертом. Таким образом чтобы пользователю было просто их описывать, но при этом сохранились "зацепки" для метаданных и дальнейших расширений. (забегая вперёд - по дальнейшему коду уже внедрять часть GUI, по крайней мере отладочную)


На правах инициатора, набросал простейший use-case - торгуем по пересечению двух MA

соответсвенно часть с input выглядит так:

/** ------- ПАРАМЕТРЫ СОВЕТНИКА ------
**/
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;

по задумке, следующее что должен сделать пользователь - описать какие именно данные он из input получает. Хотя-бы перечислить:

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

и объяснить как он эти данные вычисляет/получает (получилось длинно, но сразу на оба терминала)

/// вычисление данных
/// эксперт будет обращаться к функции каждый раз когда ему необходимы данные
/// управление кешированием и очерёдность(взаимозависимость) вычислений лежит на верхнем уровне
/// @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
}

и наконец описать торговый сигнал :

/// генерация(вычисление) сигнала при открытии бара
/// @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;
}

В ПРИНЦИПЕ ВСЁ. Этого достаточно чтобы реализовать советник и это явно не требует от пользователя чтения тонн документации. От него нужен базовый уровень владения MQL. А всё остальное должна делать библиотека (или вот, модное слово - engine). Это она должна строится под юзера, а не он учить очередной многотомник API

кстати вот 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, я пришёл к выводу, что всё это многообразие в подавляющем своём большинстве строится на фактически одних и тех же...
Vladimir Karputov
Модератор
168967
Vladimir Karputov  
Пожалуйста вставьте все свои коды правильно: ну невозможно смотреть на это серое уныние. Есть же четкий план действий: нажали на кнопку в редакторе, в полученное поле вставить код. Вы же упорно вставляете полотно текста и потом пытаетесь применить к этому полотну стиль «код»
Maxim Kuznetsov
19196
Maxim Kuznetsov  
Vladimir Karputov:
Пожалуйста вставьте все свои коды правильно: ну невозможно смотреть на это серое уныние. Есть же четкий план действий: нажали на кнопку в редакторе, в полученное поле вставить код. Вы же упорно вставляете полотно текста и потом пытаетесь применить к этому полотну стиль «код»

он (код) там криво вставляется. один фрагмент ещё можно как-то бороть, а чуть более - уже мука..

то есть про "серое уныние", это не ко мне - это к веб-мастерам. Как, в 2019г, при обилии средств, они этого добились - загадка :-)

но чтобы впредь было веселее, более-менее большие посты буду сначала писать в вики, а сюда переносить копи-пастой и снижу объём публикуемого кода.

Vitaly Muzichenko
12228
Vitaly Muzichenko  
Maxim Kuznetsov:

он (код) там криво вставляется. один фрагмент ещё можно как-то бороть, а чуть более - уже мука..

то есть про "серое уныние", это не ко мне - это к веб-мастерам. Как, в 2019г, при обилии средств, они этого добились - загадка :-)

но чтобы впредь было веселее, более-менее большие посты буду сначала писать в вики, а сюда переносить копи-пастой и снижу объём публикуемого кода.

Видимо нужно сейчас скопировать блок с кодом и вставить в "Блокнот"

Потом скопировать с блокнота и вставить назад, но перед этим создать новый блок для "нового" кода 

Vladislav Andruschenko
197020
Vladislav Andruschenko  

я создавал основу шаблон для советника и отдельно делал файл стратегий. 

Вы думаете пользователю все просто?

минимальные знания программирования все равно нужны! 

И никакие "справки" инструкции, видео, ничего не спасает. 

Пользователю потом нужно будет от Вас, чтобы Вы педалили ему бесплатно стратегии. 

И читать они справку не будут. 

Maxim Kuznetsov
19196
Maxim Kuznetsov  
Vitaly Muzichenko:

Видимо нужно сейчас скопировать блок с кодом и вставить в "Блокнот"

Потом скопировать с блокнота и вставить назад, но перед этим создать новый блок для "нового" кода 

немного помучался, но вроде как "расцветил" :-)

камешек (точнее булыжничек) в огород упомянутым веб-мастерам: при копи-пасте во встроенный редактор, точнее очевидно при первой "расцветке", редактор самовольно выкусывает часть кода который ему не нравится. В частности он самовольно редактировал  "if ( (ret=ea.OnInit())!=INIT_SECEEDED) {..}" . Видимо дивный алгоритм highlight считает что OnInit только один и перегружать в классе его нельзя. 

Stanislav Korotky
30008
Stanislav Korotky  
Это псевдокод или уже для применения? В функции GetData нельзя использовать "статики" для "хэндлов" индикаторов (d_fast_ma, d_slow_ma), потому что пользователь возьмет еще пару "машек" для фильтрации или еще чего с другими параметрами (на другом символе, например). Нужно либо кешировать "хэндлы" в каком-то объекте, либо (если на эффективность не смотреть) получать "хэндл" каждый раз заново - они должны кешироваться самим терминалом при равных параметрах.
Vasiliy Sokolov
40129
Vasiliy Sokolov  
Maxim Kuznetsov:

конечно, перед тем как вообще приступать надо установить "линию партии" и "цель-коммунизм" :

1) пользователю для реализации стратегии достаточно 100 строк. (помимо комментариев, input и прочих #property).

2) при этом число новых для него "сущностей" (функций/классов/методов/констант) должно сводится к минимуму.

3) библиотека должна содержать счётное кол-во файлов.

4) потенциально пригодна для GUI

5) расширяется за счёт плагинов


3) Ну а какая разница сколько файлов содержит библиотека? 1, 10, 100, 1000? Все должно быть сделано для удобства того, кто эту либу разрабатывает. Если ему удобно все разместить по мелким файлам - пожалуйста. Если привык все кодить в одном - пожалуйста. По большому счету не составляет труда собрать мегафайл из кучи разрозненных автоматическими средствами. Так что на счет этого пункта я бы не упорствовал. 

4) Пригодна для GUI - не совсем понятно, как это. Библиотека это изолированный слой бизнес-логики если по-взрослому. Этот слой не должен зависит от внешнего GUI вообще никак. Это GUI должно зависить от этого слоя.

Vasiliy Sokolov
40129
Vasiliy Sokolov  
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);  


У Вас изначально подход чисто процедурный: что вижу, о том пою. А если среднюю нужно рассчитать не на ценах, а допустим на объеме, или другом индикаторе? Снова заставлять переписывать пользователя? Расчет средней - это алгоритм. То к чему Вы хотите применить алгоритм - это данные. Вы смешиваете алгоритм с данными, вместо того что бы сразу это разграничить (проде этого псевдокода):

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

Ой, индекс серии забыл. - А на самом деле его и не должно быть. Нужно все рассчитывать в кольцевом буфере комбинируя алгоритмы в конвейеры.

Vasiliy Sokolov
40129
Vasiliy Sokolov  
Maxim Kuznetsov:

на правах контр-тезиса и полемики с https://www.mql5.com/ru/articles/5654 и советниками г-на Карпутова

Мой не заслужено забыли. А зря, там много чего.

Maxim Kuznetsov
19196
Maxim Kuznetsov  
Vasiliy Sokolov:

У Вас изначально подход чисто процедурный: что вижу, о том пою. А если среднюю нужно рассчитать не на ценах, а допустим на объеме, или другом индикаторе? Снова заставлять переписывать пользователя? Расчет средней - это алгоритм. То к чему Вы хотите применить алгоритм - это данные. Вы смешиваете алгоритм с данными, вместо того что бы сразу это разграничить (проде этого псевдокода):

Ой, индекс серии забыл. - А на самом деле его и не должно быть. Нужно все рассчитывать в кольцевом буфере комбинируя алгоритмы в конвейеры.

значительная часть всего ради этого и делается. Чтобы можно было считать всё по всему и советник сам разбирался в последовательностях и взаимосвязях. Я когда-то делал табличный калькулятор а-ля микро-эксель на MT4 и организацию вычислений беру по образу и подобию.

в use-case стиль принципиально процедурный, как самый распространённый. Потенциальные пользователи (начинающие программисты) именно так пишут. 


Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий