顾问的项目 - 页 4

 
Vitaly Muzichenko:

我也是,但我很久以前就得出结论,代码应该被压缩在从未被查看过的地方,它从未被纠正过,也不会被纠正。

用所有这些插槽分散用户代码是一个额外的头痛问题,因为你需要将文件拖 放到不同的终端或分享它们。当然,你可以把includniks转移到所有的终端,但如果你在一个终端中改变或增加了一些东西,那么所有的终端都必须用新的终端替换。

专家顾问和指标非常小,没有必要将它们与程序主体分开。更正确地说,它们并不小,它们是单一的文件,它不像一个有10000个页面的网站,你不能没有类和嵌套。此外,现在有了结构,而且它们足以写出紧凑和100%可行的代码。

我们来了....你知道文件夹的符号链接吗?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

我的所有库都在一个文件夹中,在一堆终端中,有几十个,在mql*/includes文件夹中,有到这个真实文件夹的符号链接。没有什么需要被拖到任何地方。

另外,我积极使用存储,如果我把所有重要的东西都放在那里,我可以在5秒钟内下载到另一个终端。但对于lib来说,符号链接更方便,总是完全同步。

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
我建议对MQL文件夹使用符号链接或结点链接。所有终端都会在同一个文件夹中查找。

并与他人分享?

 
Vitaly Muzichenko:

与人分享?

那么,你必须决定什么对你更重要,是检查还是开车)。对你来说,与人分享的便利比编码的舒适更重要吗?如果说,你在一些被许多专家顾问 使用的函数中发现了一个错误,那么你将不得不进入每一个函数的代码中,重写这个函数--作为一个程序员,这难道不令你烦恼吗?

 
谢谢大家对我的问题进行讨论。
决定先在mqlx中寻找OOP,把你的(可重复的)函数放在一个(几个)单独的文件中。也不要懒得发表意见。

还有一个 "+"是指Windows中的符号链接!我在linux中使用过它,但在Windows中忘记了它。我必须要试一试......
 
Alexey Navoykov:

你必须决定什么对你更重要,是检查还是开车)。对你来说,与人分享过程的简单性是否比编码的舒适性更重要?例如,如果你在一个函数中检测到一个错误,而这个函数被许多专家顾问使用,你将不得不进入每一个函数的代码中,重写这个函数--作为一个程序员,这难道不令你烦恼吗?

我只遇到过一次这样的情况,大约一个月前,但我不得不在那里增加检查市场的开放性,除了这个,其他都有检查,而且自从我使用以来,它第一次跳出来了。

如果需要添加一些东西,我就把它添加到当前的程序中,之后我把这个文件作为下一个程序的模板。结果是,在几年内,模板拥有一切,好吧,或者说几乎拥有一切,这样,任何复杂的机器人都可以在半小时内写好。

整个可执行代码适合于一个屏幕,虽然文件包含4000多行,但我很少看那里,如果只是我需要添加的东西。从函数循环中拒绝,只用了两个,一个是关于开放收集信息,第二个是关于历史,所有这些都在代码底部的结构中。一切都非常简单,而且相互之间很接近。主代码有注释。该项目 非常容易和迅速地扩展,没有任何损失。

 
Alexey Volchanskiy:

看起来不错,我们还能看到TRACE_***和ASSERT吗?

嗯...对于我以黑色羡慕的方式羡慕的勾引女人的大师班的作者,不客气。

如果定义了相应的系统宏,调试版本在我的代码中就会自动启用。然而,如果没有启用,你也可以通过定义来启用断言和跟踪。

#define _FORCETRACE 1
#define _FORCEASSERT 1

在这种情况下,无论系统设置如何,都会产生调试痕迹和调试断言。

我有一个指令来连接这些宏。

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

这条指令将所有必要的文件和定义挂起。我把它们都放在一个单独的Debug或Release文件夹中。把它们附在这里。(这些代码是很久以前写的,大部分是 "草率 "的,所以没有接口和历史类那么漂亮)。调试版的断言和跟踪本身在AssertD和TraceD文件中,真正的函数是PerformAssert()和PerformTrace()。

除了这些文件和宏使用全局日志文件(如果设置了输出到日志文件),我已经发布了一次,但是,再一次。日志文件在我的 "公共 "文件夹中

附加的文件:
 
Andrey Kisselyov:

干得好,我喜欢,但我不喜欢OOP,并尽量不使用它。 我不喜欢有分流的处理器(例如,4个核心和8个线程)。 应该清楚,分流和任何虚拟化对其实现来说都是性能的损失和机器时间的损失,无论是内核的分流还是代码中函数的虚拟化。

简洁是人才的姐妹,我认为听起来更好。

很久以前我就知道,代码的可维护性和重复使用远比降低性能重要。

OOP--当我在一段时间后回过头来修改代码时,它对我帮助很大。更不用说重复使用了。

但是,我同意,远非总是需要使用OOP

比如,我有CDataProvider:pulic CDataProviderI类--数据提供者,它为专家提供时间序列、指标、终端和环境数据。在一个专家顾问中,可以有许多TS--它们中的每一个都将从数据提供者那里接收指向时间序列和指标的指针(每个TS不需要创建时间序列--数据提供者将提供指向必要的时间序列的指针,如果它们已经存在,并且将只创建尚未创建的时间序列)。

当你需要从数据提供者那里获得一个指标时--填写指标描述结构,然后从提供者那里请求一个指标,指向这个结构。

因此,数据提供者内部的每个指标应该能够识别其结构(数据提供者只 "知道 "结构的抽象基类)并根据它创建准备好的指标对象,这将由数据提供者创建。

但是,仅仅为了检查一个指标的新想法而制造所有这些东西是不合理的。因此,对于这样的新指标,一切都 "自制",没有任何OOP。然而,如果我看到一个指标对专家顾问系统有用,它就会被 "正确 "地写出来--有完整的OOP支持,并在数据提供者内部创建。

P.S.

顺便说一下,在指标和数据提供者的情况下,我们看到了虚拟化继承的优势。 我们有指标参数的基本接口 CIndicatorParametersI,这个接口的继承者是必要的指标的真实参数。在请求指标时,我们声明这些参数并向数据提供者传递一个指向抽象接口的指针。因此,数据提供者本身甚至不知道请求的是什么指标--它被定义在一个函数中,其中的指标是根据新类型创建的。而且只有这个创建的指标知道传递了什么参数,它从传递的对象中检索所需的参数。

诀窍在于,在数据提供者内部几乎所有地方都有一个简单的基本参数类(或指标)--只有基本接口的最简单和最常见的功能对数据提供者可用。这简化了代码的修改(当需要时),并且不会产生 "篡改 "数据提供者的指标代码的诱惑。如果你想改变一个指标,只能在指标内部进行,数据提供者只是一个指标的存储,它最多只能创建一个新指标。

 
George Merts:

顺便说一句,当嵌套超过两层时,我就会非常紧张。 我尽量不这样写,把代码分散到各个函数上。

而且,即使有两个嵌套层次--我总是在每个闭合小括号后面写上注释,说明它关闭的是哪个块(例如,重复的循环头)。

至于风格,这是我为MT5选择历史仓位 的代码(通过指定的魔术师、符号,并指定日期范围)。

历史类本身是CTradeHistoryI抽象接口的后裔。

通过选择所需的历史 - 你可以重新计算其组件(MT5的头寸或MT4的订单),并获得任何组件的抽象接口。

对于MT4有相应的历史类,它们也继承自这些接口--因此同时提供了跨平台性--EA不需要找到它的工作地点,所有与历史的工作都是通过抽象接口完成的。


没有太多的批评。

class CTradePosComponentI: public CMyObject
{
...
}

既然有一个大家都能理解的标准CObject,为什么还要以CMyObject的形式重新发明车轮呢?

class class CTradeHistoryI: public CMyObject
{
// Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
}

CObject和CArrayObj的功能被清楚地复制到这里。为什么?快速排序是建立在标准数据容器中的。你应该使用它们。

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
}

如果这个类有一个接口--你最好把它的构造函数隐藏在保护部分。那么它的对象就不能被直接创建。

定义一个空的构造函数?我不知道。我不会这样做。如果你不需要的话,最好不要提析构器。

for(iI=0;iI<iHistoryDealsTotal; ++iI)
...

非标准的增量iI,非标准的迭代++iI,iHistoryDealsTotal--定义在外面的某个地方,远在循环之前。更加简单。

for(int i = 0; i < HistoryDealsTotal();i++)

它和以前的版本一样快,但更明显。

virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };

程序员们自己似乎反对这样的文本,但他们自己却在某个地方写了这些文本。没有人愿意去研究这种无稽之谈。是什么阻止了他们这样写。

virtual bool IsTPCInUnloss() const
{
   if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE)
      return(false);
   if(GetTPCType() == POSITION_TYPE_BUY)
   { 
      if(GetTPCStopLoss() >= GetTPCOpenPrice())
         return(true);
   } 
   else
   {
     if(GetTPCStopLoss() <= GetTPCOpenPrice())
        return(true);
   }; 
   return (false);
};

还有大括号末尾的';'--这已经过时了,你现在不应该这样做。

一个巨大的选择方法由一个巨大的for循环组成。

for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      ...
}

显然,所有对交易是否符合当前专家顾问的检查应该在一个单独的方法中实现,比如说像这样。

for(iI=0;iI<iHistoryDealsTotal; ++iI)
{
   if(!CheckDeal(iI))
      continue;
   ...
}

而一般来说,选择应该再分成3-4个方法,使其清楚地了解其中的情况。

George Merts
专家顾问本身由五行组成。在这个文件中,声明了EA的零件工厂本身的对象,并包括了内含物。

工厂是一个非常有争议的模式。使用它很好,但我不建议通过工厂做所有事情。

George Merts
而且,即使有两层嵌套,也必须在每个闭合小括号后写上注释,它埋葬了哪个块 (例如,重复的循环头)。

好吧,这就是为什么你用MQL的丑陋方式在括号里写它。如果你这样写的话。

if(OrderSelect())
{
   ...
}

你总是会看到哪个括号关闭了哪个代码块。

你可以在你的代码中发现十多个警告。当然,代码并不完美,但你可以感受到作者对美的品味:))。

 
Vasiliy Sokolov:

有点批评的意思。

О.这就是我喜欢的讨论。所以。

既然有一个大家都能理解的标准CObject,为什么还要以CMyObject的形式重新发明车轮呢?

CObject和CArrayObj的功能显然被复制到这里。为什么?快速排序是建立在标准数据容器中的。使用它们。

CMyObject是标准CObject的继承者,我代码中的所有列表和数组都是CArray(以及其他标准库中 的数组)的后代。我几乎没有使用过标准的数组[]阵列。

当然,排序和处理列表也使用了CObject的基本功能。

它们之间的区别是:标准CObject是 "一个列表或排序的数组对象"。一个CMyObject是一个具有某种类型的CObject,并包含一些创建时给出的值。我需要这个对象,因为普遍存在着将对象还原为基本抽象类的情况--通过指针了解哪个对象 "实际 "指向。CMyObject的类型正是由该函数SetMyObjectType()设置的。这个函数必须在任何从CMyObject派生的构造函数中被调用,以便为对象所属的类分配一个标识符。

它还有SetUDCreationValue() - 在创建时设置一个用户定义的值。很少使用。需要它来区分同一类别的不同对象。

如果类的接口--它的构造函数最好隐藏在保护部分。那么它的对象就不能被直接创建。

受保护的构造函数?是的,我想这对接口来说是合理的,我不知道这有可能。

定义一个空的构造函数?我不知道。我不会那样做。如果你不需要的话,最好不要提销毁器。

这是一个 "被诅咒的过去的遗产"。很久以前,我们写了一个相当大的项目,如果我们不定义一个空的析构器,出于某种原因,要花相当长的时间来删除对象。所以,从那时起,我一直在随心所欲地做这件事。一般来说,析构器也必须是虚拟的。

非标准的增量iI,非标准的迭代++iI,iHistoryDealsTotal--定义在外面的某个地方,远在循环之前。最好是保持比较简单。

不同意。增量是完全正常的,--i,只是标准化的符号--先用小字母表示其类型是整数,然后用大写字母表示其名称是I。

你似乎反对这样的床单,但你在什么地方写了。没有人愿意分析这种无稽之谈。是什么阻止了你这样写。

在这种情况下,我不得不在类的 "可见性 "和功能的美观之间做出选择。我选择了 "可见性"。美人受苦。

巨型选择法由一个巨型for循环组成。

显然,所有对交易是否符合当前专家顾问的检查应该在一个单独的方法中进行,例如像这样。

而一般来说,选择需要再分成3-4个方法,以使其明确其中的内容。

我同意。在这里,原则上,这个非常循环已经 "成长 "了,起初它并没有那么大。

尽管在私有函数中实现次要的检查并不总是很方便,因为这些检查在代码中并不总是可追踪的。

工厂是一个非常有争议的模式。使用是可以的,但我不建议通过工厂做所有的事情。

现在我不记得了,当时有几种建立专家顾问的变体。我在 "专家顾问零件工厂 "停了下来。原则上,它不再是一个纯粹的经典 "工厂 "模式。最初,它打算成为一个 "经典 "模式,但现在它更像是一个专家顾问部件的 "构造器-集中器"。而且它还负责拆除这些部件,这不是工厂的特点。但这个名字依然存在。

这就是为什么你用MQL的丑陋方式在括号里写它。如果你这样写的话。

你总是能看到哪个括号关闭了哪个代码块。

为什么是 "丑陋的方式"?

循环标题,然后是缩进的开头括号,然后是同样缩进的整个块,最后是结束括号--也是缩进的。

你认为什么是更好的?

 
Gregory Kovalenko:

我需要在2个未结订单上获得利润。上一个未平仓的订单,我称之为OrderProfit2,而下一个订单--OrderProfit1

先开一阶,再开二阶,所以循环中的一阶被称为2)

错误在哪里?

你只是通过订单。它没有任何地方检查哪个是第一,哪个是第二。

你需要输入一个关于开放时间 的检查。这样你就可以区分出较早开盘的订单和较晚开盘的订单。或者说,他们可以同时打开。