更少的代码,更多的行动......写一个EA

 

我将尝试(或我们将尝试,如果有人感兴趣的话)为专家顾问做一个框架。尽可能只适合于简单的事情,而且不需要应用程序员的任何实质性知识。

与当地接受的做法不同,设计将从上到下进行。换句话说,首先我们为终端用户编写EA的主要(也是唯一)文件,删除所有明显不必要的东西,并编写一个库来使之工作。 然后我们增加一个新的用例,并升级这个库。

作为与https://www.mql5.com/ru/articles/5654 和卡尔普托夫先生的顾问们的反驳和论战

当然,在着手进行之前,有必要确定 "党的路线 "和 "目标--共产主义"。

- 100行足以让用户实施该策略。(除了评论、输入和其他#属性之外)。

- 新 "实体"(函数/类/方法/常量)的数量应减少到最低限度。

- 该库必须包含一个可计算的文件数量。

- 有可能适用于GUI

- 可通过插件扩展

目标可以实现吗?很难,但原则上是的。有一些方法,也有一些想法。但目前还没有现成的解决方案 :-)

目前的步骤是组织专家获取数据。以这样一种方式,用户很容易描述它们,但同时仍有元数据和进一步扩展的 "线索"。(今后--GUI部分,至少是调试部分,应该由进一步的代码来实现。)


作为发起人,我创建了一个简单的用例--我们在两个MA的交叉点进行交易。

相应地,输入部分看起来像这样

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

用户要做的下一件事是--描述他从输入中获得了什么数据。至少要列出它们。

// просто перечисляем идентификаторы данных
// всех которые нужны для принятия решений
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;
}

基本上就是这样了。这足以实现一个EA,而且它显然不需要用户阅读大量的文档。用户只需要有MQL的基本知识。其余的都应该由库(或者这里有一个花哨的词--引擎)来完成。它应该是为用户建立的,而不是让用户去学习另一个多卷的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:
请正确插入你的所有代码:不可能看着这灰色的阴暗面。有一个明确的行动计划:点击编辑器中的按钮,在结果字段中插入代码。你顽固地插入一个文本画布,然后试图将 "代码 "样式应用到这个画布上

它(代码)没有正确插入。 一个片段还可以处理,但多一点就已经很麻烦了......

我的意思是,这不是由我决定的--而是由网站管理员决定的。2019年,在资金充裕的情况下,他们是如何做到这一点的--这是一个谜 :-)

但为了今后更有乐趣,我会先在wiki中写出或多或少的大文章,然后在这里复制粘贴,减少发布代码的量。

 
Maxim Kuznetsov:

它(代码)没有正确地插入那里。 一个片段还可以处理,但多一点就已经很麻烦了......

我的意思是,这不是由我决定的--而是由网站管理员决定的。2019年,在资金充裕的情况下,他们是如何实现的--这是个谜 :-)

但为了今后有更多的乐趣,或多或少的大文章会先写到wiki上,然后通过复制粘贴转到这里,减少发布代码的量。

显然,你现在必须复制代码块并将其粘贴到记事本中。

然后从记事本中复制并粘贴回来,但在此之前为 "新 "代码创建一个新块

 

我为EA创建了一个基本模板,并单独制作了策略文件。

你认为这对用户来说容易吗?

你仍然需要有最起码的编程知识!

而且没有 "帮助 "说明、视频,没有任何东西可以拯救你。

然后,用户将需要你免费提供踏板策略。

而且他们不会阅读帮助。

 
Vitaly Muzichenko:

显然,你现在应该复制这段代码,并将其粘贴到记事本上

然后从记事本中复制并粘贴回来,但在此之前为 "新 "代码创建一个新块

花了点麻烦,但算是给它 "上了色"。:-)

给上述网站管理员的一块鹅卵石(或者说是一块鹅卵石):在内置编辑器中粘贴时,或者更明显的是在第一次 "上色 "时,编辑器会弹出它不喜欢的某些部分的代码。特别是,它任意编辑了 "if ((ret=ea.OnInit())!=INIT_SECEED) {...}" 。显然,高亮算法认为OnInit是唯一的,它不能在类中被重载。

 
这是个伪代码还是已经用于应用?GetData 函数不能使用指标 "句柄"(d_fast_ma,d_slow_ma)的 "静态",因为用户会拿另外几个 "句柄 "进行过滤或其他不同参数的东西(例如在另一个符号上)。我们应该把 "句柄 "缓存在某个对象中,或者(如果不看效率的话)每次都得到 "句柄"--当参数相等时,它们应该由终端自己缓存起来。
 
Maxim Kuznetsov:

当然,在进行这些工作之前,有必要确定 "党的路线 "和 "共产主义目标"。

1)用户只需要100行就可以实现该策略。(除了评论、输入和其他#属性之外)。

2)应该尽量减少它的新 "实体"(函数/类/方法/常量)的数量。

3)图书馆必须包含可计算的文件数量。

4) 有可能适合GUI

5)可通过插件扩展


3) 一个库包含多少个文件有什么区别?1, 10, 100, 1000?一切都应该为开发商的方便而做。如果他愿意把所有东西都放在小文件里,请随意。如果他/她习惯于在一个文件中编码所有内容,请随意。总的来说,从一堆不相干的自动化手段中组建一个巨型文件并不困难。所以我不会坚持这一点。

4) GUI可用 - 不太清楚它是怎样的。该库是一个成熟的商业逻辑的隔离层。这一层不应该以任何方式依赖于外部GUI。它的GUI应该依赖于这个层。

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

哦,我忘了系列索引。- 真的不应该有。你必须通过将算法组合成流水线来计算环形缓冲器 中的所有内容。

 
Maxim Kuznetsov:

作为对https://www.mql5.com/ru/articles/5654 和卡尔普托夫先生的顾问们的反驳和论证

我的已经被无缘无故地遗忘了。而且徒劳无功,那里有很多东西。

 
Vasiliy Sokolov:

你的方法从一开始就是纯粹的程序性的:我所看到的就是我所讲的。 如果平均数的计算不是基于价格,而是基于成交量或其他指标呢?我 们必须再次重写用户吗?平均数的计算是一种算法。你想应用算法的是数据。你把算法和数据混在一起,而不是马上把它分开(像这样的伪代码)。

哎呀,我忘记了系列索引。- 真的不应该有。你必须通过将算法组合成流水线来计算环形缓冲器中的所有内容。

每件事的很大一部分都是为了这个目的而做的。这样,你可以计算一切,而专家顾问可以自己弄清序列和相互关系。我曾经在MT4上做过一个电子表格的计算器,就像micro-excel一样,计算的组织是基于这个模型。

用例风格从根本上说是程序性的,因为它是最常见的。潜在的用户(新手程序员)以这种方式写作。