Moins de code, plus d'action... l'écriture d'un EA

 

Je vais essayer (ou nous allons essayer, si quelqu'un est intéressé) de créer un cadre pour les conseillers experts. Le plus approprié possible pour les choses simples et qui ne nécessitent pas de connaissances substantielles de la part du programmeur appliqué.

Contrairement à la pratique acceptée localement, la conception sera menée de haut en bas. En d'autres termes, nous écrivons d'abord le fichier principal (et unique) de l'EA pour l'utilisateur final, nous supprimons tout ce qui est manifestement inutile et nous écrivons une bibliothèque pour faire fonctionner le tout.

Comme contre-thèse et polémique avec https://www.mql5.com/ru/articles/5654 et les conseillers de M. Karputov

bien sûr, avant de procéder, il faut définir la "ligne du parti" et la "cible-communisme" :

- 100 lignes suffisent à l'utilisateur pour mettre en œuvre la stratégie. (en dehors des commentaires, des entrées et des autres #propriétés).

- Le nombre de nouvelles "entités" (fonctions/classes/méthodes/constantes) doit être réduit au minimum.

- La bibliothèque doit contenir un nombre dénombrable de fichiers.

- Potentiellement adapté à l'interface graphique

- extensible par des plugins

Objectifs réalisables ? Difficile, mais en principe OUI. Il y a des moyens, des idées. Mais il n'y a pas encore de solution toute faite :-)

L'étape actuelle consiste à organiser l'acquisition des données par l'expert. De telle sorte qu'il soit facile pour l'utilisateur de les décrire, mais en même temps il y a encore des "pistes" pour les métadonnées et d'autres extensions. (A l'avenir, la partie GUI, au moins celle du débogage, devrait être implémentée par le code ultérieur).


En tant qu'initiateur, j'ai créé un cas d'utilisation simple : nous effectuons des transactions sur le croisement de deux MA.

En conséquence, la partie entrée ressemble à ceci

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

La prochaine chose à faire pour un utilisateur est de décrire les données qu'il reçoit en entrée. Enumérez-les au moins :

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

et expliquer comment il calcule/reçoit ces données (c'est long, mais cela s'applique aux deux terminaux).

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

et enfin décrire le signal commercial :

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

C'EST À PEU PRÈS TOUT. C'est suffisant pour mettre en œuvre un EA et cela ne nécessite évidemment pas que l'utilisateur lise des tonnes de documentation. L'utilisateur n'a besoin que d'une connaissance de base de MQL. Tout le reste devrait être fait par la bibliothèque (ou ici, le mot chic - moteur). Il doit être conçu pour l'utilisateur, et non pour que l'utilisateur apprenne une autre API en plusieurs volumes.

Au fait, voici 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, я пришёл к выводу, что всё это многообразие в подавляющем своём большинстве строится на фактически одних и тех же...
 
Veuillez insérer tous vos codes correctement : il est impossible de regarder cette grisaille. Le plan d'action est clair : cliquer sur le bouton dans l'éditeur, insérer le code dans le champ correspondant. Mais vous vous entêtez à insérer un canevas de texte et essayez ensuite d'appliquer le style "code" à ce canevas
 
Vladimir Karputov:
Veuillez insérer correctement tous vos codes : il est impossible de regarder cette grisaille. Le plan d'action est clair : cliquer sur le bouton dans l'éditeur, insérer le code dans le champ correspondant. Vous vous entêtez à insérer un canevas de texte et essayez ensuite d'appliquer le style "code" à ce canevas

Il (le code) n'est pas inséré correctement. Un fragment peut encore être traité, mais un peu plus est déjà une douleur...

Je veux dire, ça ne dépend pas de moi - ça dépend des webmasters. Comment, en 2019, avec l'abondance de fonds, ils y sont parvenus - un mystère :-)

Mais pour que ce soit plus amusant à l'avenir, j'écrirai d'abord des billets plus ou moins gros dans le wiki, puis je les copierai-collerai ici et réduirai le volume du code publié.

 
Maxim Kuznetsov:

Il (le code) n'est pas inséré correctement. Un fragment peut encore être traité, mais un peu plus est déjà une douleur...

Je veux dire, ça ne dépend pas de moi - ça dépend des webmasters. Comment, en 2019, avec l'abondance de fonds, ils y sont parvenus - c'est un mystère :-).

Mais pour avoir plus de plaisir à l'avenir, les postes plus ou moins importants seront d'abord écrits sur le wiki, puis transférés ici par copier-coller et réduire le volume du code publié.

Apparemment, vous devez maintenant copier le bloc de code et le coller dans le Bloc-notes.

Ensuite, copiez du Bloc-notes et collez à nouveau, mais avant cela, créez un nouveau bloc pour le "nouveau" code.

 

J'ai créé un modèle de base pour l'EA et j'ai créé le fichier de stratégies séparément.

Pensez-vous que c'est facile pour l'utilisateur ?

Vous devez tout de même avoir un minimum de connaissances en programmation !

Et aucune instruction d'"aide", aucune vidéo, rien ne vous sauve.

L'utilisateur aura alors besoin de vous pour pédaler des stratégies gratuitement.

Et ils ne liront pas l'aide.

 
Vitaly Muzichenko:

Apparemment, vous devez maintenant copier le bloc de code et le coller dans le bloc-notes.

Ensuite, copiez depuis le bloc-notes et collez à nouveau, mais avant cela, créez un nouveau bloc pour le "nouveau" code.

Ça m'a pris un peu de temps, mais je l'ai en quelque sorte "coloré". :-)

Un caillou (ou plutôt un caillou) aux webmasters mentionnés ci-dessus : lors du collage dans l'éditeur inline, ou plutôt évidemment lors de la première "coloration", l'éditeur mord involontairement une partie du code qu'il n'aime pas. En particulier, il a arbitrairement édité "if ((ret=ea.OnInit())!=INIT_SECEED) {...}" . Apparemment, l'algorithme de mise en évidence croit que OnInit est le seul et qu'il ne peut pas être surchargé dans la classe.

 
S'agit-il d'un pseudo-code ou d'une application déjà existante ? La fonctionGetData ne peut pas utiliser les "statics" pour les "handles" des indicateurs(d_fast_ma,d_slow_ma), car l'utilisateur prendra un autre couple de "handles" pour le filtrage ou autre chose avec des paramètres différents (sur un autre symbole, par exemple). Il faut soit mettre en cache les "handles" dans un objet quelconque, soit (si l'efficacité n'est pas recherchée) récupérer les "handles" à chaque fois - ils devraient être mis en cache par le terminal lui-même lorsque les paramètres sont égaux.
 
Maxim Kuznetsov:

bien sûr, avant de procéder, il faut définir la "ligne du parti" et la "cible-communisme" :

1) l'utilisateur n'a besoin que de 100 lignes pour mettre en œuvre la stratégie. (outre les commentaires, les entrées et autres #propriétés).

2) Le nombre de nouvelles "entités" (fonctions/classes/méthodes/constantes) doit être réduit au minimum.

3) la bibliothèque doit contenir un nombre dénombrable de fichiers.

4) potentiellement adapté à l'interface graphique

5) extensible par des plugins


3) Quelle différence cela fait-il de savoir combien de fichiers une bibliothèque contient ? 1, 10, 100, 1000 ? Tout doit être fait pour la commodité du développeur. S'il est à l'aise pour tout placer dans de petits dossiers, n'hésitez pas. S'il a l'habitude de tout coder dans un seul fichier, n'hésitez pas. En général, il n'est pas difficile d'assembler un méga-fichier à partir d'un ensemble de moyens automatisés disparates. Je n'insisterais donc pas sur ce point.

4) GUI utilisable - ce n'est pas tout à fait clair comment c'est. La bibliothèque est une couche isolée de la logique commerciale de manière mature. Cette couche ne doit en aucun cas dépendre de l'interface graphique externe. Son interface graphique devrait dépendre de cette couche.

 
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);  


Votre approche est d'emblée purement procédurale : ce que je vois est ce que je raconte. Que faire si la moyenne doit être calculée non pas sur les prix, mais, disons, sur le volume, ou un autre indicateur ? Devons-nous réécrire l'utilisateur à nouveau ? Le calcul de la moyenne est un algorithme. Ce que vous voulez appliquer à l'algorithme, ce sont les données. Vous mélangez l'algorithme avec les données, au lieu de les séparer d'emblée (comme ce pseudo-code) :

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

Oh, j'ai oublié l'index des séries. - Il ne devrait vraiment pas y en avoir. Vous devez tout calculer dans un tampon circulaire en combinant les algorithmes en pipelines.

 
Maxim Kuznetsov:

Comme une contre-thèse et une polémique à https://www.mql5.com/ru/articles/5654 et aux conseillers de M. Karputov

Lemien a été injustement oublié. Et en vain, il y a beaucoup de choses.

 
Vasiliy Sokolov:

Votre approche est d'emblée purement procédurale : ce que je vois est ce que je raconte. Que faire si la moyenne doit être calculée non pas sur les prix, mais, disons, sur le volume, ou un autre indicateur ? Devons-nous réécrire l'utilisateur à nouveau ? Le calcul de la moyenne est un algorithme. Ce que vous voulez appliquer à l'algorithme, ce sont les données. Vous mélangez l'algorithme avec les données, au lieu de faire une distinction (comme ce pseudo-code) :

Oh, j'ai oublié l'index des séries. - Il ne devrait vraiment pas y en avoir. Vous devez tout calculer dans un tampon circulaire en combinant les algorithmes en pipelines.

Une grande partie de tout est faite pour cela. Ainsi, vous pouvez tout calculer et le conseiller expert peut déterminer lui-même les séquences et les interrelations. J'ai fait une fois un tableur calculateur à la micro-excel sur MT4, et l'organisation des calculs est basée sur ce modèle.

Le style cas d'utilisation est fondamentalement procédural, car il est le plus courant. Les utilisateurs potentiels(programmeurs novices) écrivent de cette manière.


Raison: