Menos código, mais ação... escrever um EA

 

Vou tentar (ou tentaremos, se alguém estiver interessado) fazer uma estrutura para Consultores Especialistas. O mais adequado possível apenas para coisas simples e que não requerem nenhum conhecimento substancial por parte do programador aplicado.

Em contraste com a prática aceita localmente, o projeto será conduzido de cima para baixo. Em outras palavras, primeiro escrevemos o arquivo principal (e o único) do EA para o usuário final, removemos tudo o que é obviamente desnecessário, e escrevemos uma biblioteca para que isto funcione. Depois adicionamos um novo caso de uso e atualizamos a biblioteca.

Como uma contra-tese e polêmica com https://www.mql5.com/ru/articles/5654 e os conselheiros do Sr. Karputov

é claro que, antes de prosseguir, é necessário estabelecer a "linha do partido" e o "comunismo alvo":

- 100 linhas é suficiente para que o usuário possa implementar a estratégia. (além de comentários, entradas e outras #propriedades).

- O número de novas "entidades" (funções/classes/métodos/constantes) deve ser reduzido ao mínimo.

- A biblioteca deve conter um número contável de arquivos.

- Potencialmente adequado para GUI

- expansível por plugins

Objetivos realizáveis ? difíceis, mas em princípio SIM. Há algumas maneiras, algumas idéias. Mas ainda não há uma solução pronta :-)

O passo atual é organizar a aquisição de dados pelo especialista. De tal forma que é fácil para o usuário descrevê-los, mas ao mesmo tempo os "leads" para metadados e outras extensões são preservados. (Prosseguindo - a parte da GUI, pelo menos a debugging, já deve ser implementada em código adicional)


Como iniciador, criei um caso de uso simples - nós negociamos na passagem de dois MA

De forma correspondente, a parte de entrada se parece com isto

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

A próxima coisa que um usuário tem que fazer - descrever quais dados ele recebe da entrada. Liste-os, pelo menos:

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

e explicar como calcula/recebe estes dados (longos, mas para ambos os terminais).

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

e, finalmente, descrever o sinal comercial:

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

ISSO É BASICAMENTE TUDO. Isto é suficiente para implementar uma EA e obviamente não requer que o usuário leia toneladas de documentação. O usuário só precisa de um conhecimento básico de MQL. Todo o resto deve ser feito pela biblioteca (ou aqui, uma palavra chique - motor). Ele deve ser construído para o usuário, não para o usuário aprender outro API multi-volume.

A propósito, aqui está o 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, я пришёл к выводу, что всё это многообразие в подавляющем своём большинстве строится на фактически одних и тех же...
 
Por favor, insira todos os seus códigos corretamente: é impossível olhar para este entorpecimento cinzento. Há um plano de ação claro: clique no botão do editor, insira o código no campo resultante. Mas você teima em inserir uma tela de texto e depois tenta aplicar o estilo "código" a essa tela
 
Vladimir Karputov:
Por favor, insira todos os seus códigos corretamente: é impossível olhar para esta escuridão cinzenta. Há um plano de ação claro: clique no botão do editor, insira o código no campo resultante. Você insere teimosamente uma tela de texto e depois tenta aplicar o estilo "código" a essa tela

Ele (o código) não está inserido corretamente. Um fragmento ainda pode ser tratado, mas um pouco mais já é uma dor...

Quero dizer, não depende de mim - depende dos webmasters. Como, em 2019, com a abundância de fundos, eles conseguiram isso - um mistério :-)

Mas para torná-lo mais divertido no futuro, primeiramente escreverei mensagens mais ou menos grandes no wiki, e depois as copiarei coladas aqui e reduzirei o volume do código publicado.

 
Maxim Kuznetsov:

Ele (o código) não está inserido corretamente. Um fragmento ainda pode ser tratado, mas um pouco mais já é uma dor...

Quero dizer, não depende de mim - depende dos webmasters. Como, em 2019, com a abundância de fundos, eles o conseguiram - é um mistério :-)

Mas, para se divertir mais no futuro, serão escritos primeiramente no wiki, mais ou menos grandes posts, e depois transferidos para cá por copy-paste e reduzir o volume do código publicado.

Aparentemente, você tem que copiar o bloco de código e colá-lo no "Bloco de Notas" agora

Depois copiar do bloco de notas e colar de volta, mas antes disso criar um novo bloco para o "novo" código

 

Eu criei um modelo básico para a EA e fiz o arquivo de estratégia separadamente.

Você acha que é fácil para o usuário?

Você ainda precisa de um conhecimento mínimo de programação!

E sem instruções de "ajuda", vídeos, nada o salva.

O usuário precisará então de estratégias de pedalar gratuitamente.

E eles não vão ler a ajuda.

 
Vitaly Muzichenko:

Aparentemente, você deve agora copiar o bloco de código e colá-lo no Bloco de Notas

Depois copie do bloco de notas e cole de volta, mas antes disso crie um novo bloco para o "novo" código

Levou um pouco de problemas, mas tipo "colorido". :-)

Uma pedrinha (ou melhor, uma pedrinha) para os webmasters mencionados acima: ao colar no editor em linha, ou melhor, obviamente no início "colorir", o editor, sem querer, morde uma parte do código de que não gosta. Em particular, ele foi editado arbitrariamente "se ((ret=ea.OnInit())!=INIT_SECEED) {..}" . Aparentemente, o algoritmo de destaque acredita que o OnInit é o único e não pode ser sobrecarregado na classe.

 
Este pseudo-código é ou já é para aplicação? A funçãoGetData não pode usar "estática" para o indicador "handles"(d_fast_ma,d_slow_ma), porque o usuário levará outro par de "handles" para filtragem ou algo com parâmetros diferentes (em outro símbolo, por exemplo). Deve-se "manejar" o cache em algum objeto ou (se a eficiência não for vista) "manejar" todas as vezes - eles devem ser armazenados em cache pelo próprio terminal quando os parâmetros são iguais.
 
Maxim Kuznetsov:

É claro que, antes de prosseguir, é necessário estabelecer uma "linha partidária" e um "comunismo alvo":

1) o usuário só precisa de 100 linhas para implementar a estratégia. (além de comentários, entradas e outras #propriedades).

2) O número de novas "entidades" (funções/classes/métodos/constantes) para ele deve ser minimizado.

3) a biblioteca deve conter um número contável de arquivos.

4) potencialmente adequado para GUI

5) expansível por plugins


3) Que diferença faz a quantidade de arquivos que uma biblioteca contém? 1, 10, 100, 1000? Tudo deve ser feito para a conveniência do desenvolvedor. Se ele estiver à vontade para colocar tudo em arquivos pequenos, sinta-se à vontade. Se ele está acostumado a codificar tudo em um único arquivo, sinta-se livre. De modo geral, não é difícil montar um mega-arquivo a partir de um monte de meios automatizados díspares. Portanto, eu não insistiria neste ponto.

4) GUI utilizável - não está muito claro como é. A biblioteca é uma camada isolada de lógica empresarial de uma forma madura. Esta camada não deve depender de forma alguma da GUI externa. É GUI deve depender desta camada.

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


Sua abordagem é puramente processual desde o início: o que eu vejo é o que eu digo. E se a média tiver que ser calculada não sobre os preços, mas, digamos, sobre o volume, ou outro indicador? Temos que reescrever o usuário novamente? O cálculo da média é um algoritmo. O que você quer aplicar o algoritmo são os dados. Você está misturando o algoritmo com os dados, em vez de separá-los imediatamente (como este pseudo-código):

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

Oh, esqueci o índice da série. - Realmente não deveria haver um. Você tem que computar tudo em um buffer de anel, combinando algoritmos em dutos.

 
Maxim Kuznetsov:

Como contra-tese e polêmica para https://www.mql5.com/ru/articles/5654 e para os conselheiros do Sr. Karputov

O meu tem sido imerecidamente esquecido. E em vão, há muita coisa lá.

 
Vasiliy Sokolov:

Sua abordagem é puramente processual desde o início: o que eu vejo é o que eu digo. E se a média tiver que ser calculada não sobre os preços, mas, digamos, sobre o volume, ou outro indicador? Temos que reescrever o usuário novamente? O cálculo da média é um algoritmo. O que você quer aplicar o algoritmo são os dados. Você está misturando o algoritmo com os dados, em vez de separá-los imediatamente (como este pseudo-código):

Oops, eu esqueci o índice da série. - Realmente não deveria haver um. Você tem que computar tudo em um buffer de anel, combinando algoritmos em dutos.

Uma grande parte de tudo é feita por causa disso. Para que você possa calcular tudo e o próprio Expert Advisor possa descobrir as seqüências e inter-relações. Uma vez fiz uma planilha calculadora a la microexcel no MT4, e a organização dos cálculos é baseada neste modelo.

O estilo de caso de uso é fundamentalmente processual, pois é o mais comum. Os usuários em potencial(programadores novatos) escrevem desta forma.


Razão: