Menos código, más acción... escribir un EA

 

Intentaré (o intentaremos, si alguien está interesado) hacer un marco para los Asesores Expertos. En la medida de lo posible sólo para cosas sencillas y que no requieran ningún conocimiento sustancial del programador aplicado.

A diferencia de la práctica localmente aceptada, el diseño se realizará de arriba a abajo. En otras palabras, primero escribimos el archivo principal (y el único) del EA para el usuario final, eliminamos todo lo obviamente innecesario y escribimos una biblioteca para que esto funcione. Luego añadimos un nuevo caso de uso y actualizamos la biblioteca.

Como contra tesis y polémica con https://www.mql5.com/ru/articles/5654 y los asesores del Sr. Karputov

Por supuesto, antes de proceder, es necesario establecer la "línea del partido" y el "objetivo-comunismo" :

- 100 líneas son suficientes para que el usuario aplique la estrategia. (aparte de los comentarios, entradas y otras #propiedades).

- El número de nuevas "entidades" (funciones/clases/métodos/constantes) debe reducirse al mínimo.

- La biblioteca debe contener un número contable de archivos.

- Potencialmente adecuado para la interfaz gráfica de usuario (GUI)

- ampliable mediante plugins

¿Metas alcanzables? Difícil, pero en principio SÍ. Hay algunas formas de hacerlo y algunas ideas. Pero aún no hay una solución lista :-)

El paso actual es organizar la adquisición de datos por parte del experto. De tal manera que sea fácil para el usuario describirlos, pero al mismo tiempo se conservan las "pistas" para los metadatos y las extensiones posteriores. (En el futuro, la parte de la interfaz gráfica de usuario, al menos la de depuración, ya debería estar implementada en el código posterior)


Como iniciador, he creado un caso de uso simple - operamos en el cruce de dos MA

En consecuencia, la parte de entrada tiene el siguiente aspecto

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

Lo siguiente que tiene que hacer el usuario es describir los datos que recibe de entrada. Enuméralos al menos:

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

y explicar cómo calcula/recibe estos datos (largo, pero para ambos terminales).

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

y, por último, describir la señal 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;
}

ESO ES BÁSICAMENTE TODO. Esto es suficiente para implementar un EA y obviamente no requiere que el usuario lea toneladas de documentación. El usuario sólo necesita un conocimiento básico de MQL. Todo lo demás debe hacerlo la biblioteca (o aquí, la palabra elegante - motor). Debe construirse para el usuario, no para que éste aprenda otra API de varios volúmenes.

Por cierto, aquí está 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, introduzca todos sus códigos correctamente: es imposible mirar este grisáceo embotamiento. Hay un plan de acción claro: hacer clic en el botón del editor, insertar el código en el campo resultante. Pero se obstina en insertar un lienzo de texto y luego intenta aplicar el estilo "código" a este lienzo
 
Vladimir Karputov:
Por favor, introduzca todos sus códigos correctamente: es imposible mirar esta penumbra gris. Hay un plan de acción claro: hacer clic en el botón del editor, insertar el código en el campo resultante. Insertas obstinadamente un lienzo de texto y luego intentas aplicar el estilo "código" a este lienzo

Un fragmento todavía puede ser tratado, pero un poco más ya es un dolor...

No depende de mí, sino de los webmasters. Cómo, en 2019, con la abundancia de fondos, lo han conseguido - un misterio :-)

Pero para hacerlo más divertido en el futuro, primero escribiré posts más o menos grandes en la wiki, para luego copiarlos y pegarlos aquí y reducir el volumen del código publicado.

 
Maxim Kuznetsov:

Un fragmento todavía se puede manejar, pero un poco más ya es un dolor...

No depende de mí, sino de los webmasters. Cómo, en 2019, con la abundancia de fondos, lo han conseguido - es un misterio :-)

Pero para divertirnos más en el futuro, los posts más o menos grandes se escribirán primero en la wiki, y luego se trasladarán aquí mediante el copy-paste y se reducirá el volumen del código publicado.

Al parecer, ahora hay que copiar el bloque de código y pegarlo en el Bloc de notas

A continuación, copie desde el Bloc de notas y pegue de nuevo, pero antes cree un nuevo bloque para el "nuevo" código

 

He creado una plantilla básica para el EA y he hecho el archivo de estrategia por separado.

¿Cree que es fácil para el usuario?

Aún así, es necesario tener un mínimo de conocimientos de programación.

Y no hay instrucciones de "ayuda", videos, nada te salva.

El usuario necesitará entonces que usted pedalee las estrategias de forma gratuita.

Y no leerán la ayuda.

 
Vitaly Muzichenko:

Al parecer, ahora debe copiar el bloque de código y pegarlo en el Bloc de notas

Luego copie desde el bloc de notas y pegue de nuevo, pero antes cree un nuevo bloque para el "nuevo" código

Me costó un poco de trabajo, pero lo "coloreé". :-)

Una piedra (o más bien un guijarro) para los webmasters mencionados anteriormente: al pegar en el editor inline, o más bien obviamente al principio "colorear", el editor muerde involuntariamente una parte del código que no le gusta. En particular, editó arbitrariamente "if ((ret=ea.OnInit())!=INIT_SECEED) {..}" . Aparentemente, el algoritmo de resaltado cree que OnInit es el único y no puede ser sobrecargado en la clase.

 
¿Esto es un pseudocódigo o es ya para la aplicación? La funciónGetData no puede utilizar "estáticos" para los "handles" de los indicadores(d_fast_ma,d_slow_ma), porque el usuario tomará otro par de "handles" para filtrar o algo más con parámetros diferentes (en otro símbolo, por ejemplo). Uno debería almacenar en caché los "handles" en algún objeto o (si no se mira la eficiencia) obtener los "handle" cada vez de nuevo - deberían ser almacenados en caché por el propio terminal cuando los parámetros son iguales.
 
Maxim Kuznetsov:

por supuesto, antes de proceder en absoluto, es necesario establecer "línea de partido" y "objetivo-comunismo" :

1) el usuario sólo necesita 100 líneas para aplicar la estrategia. (aparte de los comentarios, entradas y otras #propiedades).

2) Se debe minimizar el número de nuevas "entidades" (funciones/clases/métodos/constantes) para ello.

3) La biblioteca debe contener un número contable de archivos.

4) potencialmente adecuado para GUI

5) ampliable mediante plugins


3) ¿Qué diferencia hay en el número de archivos que contiene una biblioteca? 1, 10, 100, 1000? Todo debe hacerse para la comodidad del promotor. Si se siente cómodo colocando todo en pequeños archivos, no dude en hacerlo. Si está acostumbrado a codificar todo en un solo archivo, no dude en hacerlo. En general, no es difícil reunir un megafichero a partir de un montón de medios automáticos dispares. Así que yo no insistiría en este punto.

4) GUI utilizable - no está muy claro cómo es. La biblioteca es una capa aislada de la lógica de negocio de forma madura. Esta capa no debe depender en absoluto de la interfaz gráfica de usuario externa. Su interfaz gráfica debería depender de esta capa.

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


Su enfoque es puramente procedimental desde el principio: lo que veo es lo que cuento. ¿Y si la media no debe calcularse sobre los precios, sino, por ejemplo, sobre el volumen u otro indicador? ¿Tenemos que volver a escribir el usuario? El cálculo de la media es un algoritmo. Lo que se quiere es aplicar el algoritmo a los datos. Estás mezclando el algoritmo con los datos, en lugar de separarlos directamente (como este pseudocódigo):

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

Oh, me olvidé del índice de la serie. - Realmente no debería haber ninguno. Hay que computar todo en un buffer de anillo combinando algoritmos en pipelines.

 
Maxim Kuznetsov:

Como contra tesis y polémica a https://www.mql5.com/ru/articles/5654 y a los asesores del Sr. Karputov

El mío ha sido inmerecidamente olvidado. Y en vano, hay mucho allí.

 
Vasiliy Sokolov:

Su enfoque es puramente procedimental desde el principio: lo que veo es lo que cuento. ¿Y si la media no debe calcularse sobre los precios, sino, por ejemplo, sobre el volumen u otro indicador? ¿Tenemos que volver a escribir el usuario? El cálculo de la media es un algoritmo. Lo que se quiere es aplicar el algoritmo a los datos. Estás mezclando el algoritmo con los datos, en lugar de hacer una distinción (como este pseudocódigo):

Oh, me olvidé del índice de la serie. - Realmente no debería haber ninguno. Hay que computar todo en un buffer de anillo combinando algoritmos en pipelines.

Gran parte de todo lo que se hace es por eso. Para que usted pueda calcular todo y el Asesor Experto pueda averiguar las secuencias e interrelaciones por sí mismo. Una vez hice una calculadora de hoja de cálculo a la micro-excel en MT4, y la organización de los cálculos se basa en este modelo.

El estilo de caso de uso es fundamentalmente procedimental, ya que es el más común. Los usuarios potenciales(programadores novatos) escriben de esta manera.