从头开始自学MQL5语言 - 页 72

 
Vasiliy Sokolov:

由于完美无缺,我将在代码上多加一些评论。

我用黄色标出了两个非琐碎的地方。

1) 注意代码在第一个if和下一个else中重复。唯一不同的是最后一行和结束动作(OpenBUY,OpenSell)。

2)进入其他区块的条件并不明显。由于有大量的?事实上,它们只取决于最后一行。

这是一个肯定的迹象,表明这里缺少一个功能。

我们需要写一个函数,如果开仓 时间与指定的时间一致,则返回真(我以后会写)。

是的,瓦西里,你是对的,我们真的应该写一个函数。

问候,弗拉基米尔。

 
Vasiliy Sokolov:
顺便说一下,要注意你的程序的总大小。它已经相当大了。你喜欢它吗?顺便说一下,一个新手不能写这么大的代码量:变量会混在一起,括号会过大,编译错误会像雨后的蘑菇一样悄然出现。编译后,这样规模的程序开始出现故障,没有人能够理解哪里出了问题。而在你的代码中,由于某种原因,一切都能正常工作),而且在函数中很清楚发生了什么,如何发生的。一句话,它是一种美。

谢谢你,瓦西里。这里有很多你的工作,因为追踪止损模板是你提供的。我只需要用代码来填充这些功能。我目前正在研究追踪止损。有些事情已经做了,但为了向公众展示最终的EA版本,有些事情我还需要了解。

问候,弗拉基米尔。

 

我添加了一些功能。我最后的代码是这样的。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Зададим условия для открытия позиций BUY и SELL   
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   price=NormalizeDouble(price,digits);
   //-- Если открытой позиции нет и время для открытия позволяет,
   //-- открывает BUY или SELL в зависимости от положения тика
   if(IsMainPositionOpen() == false && IsTimeForOpen())
   {
      if(TickUP()==(price+point))
         OpenBUY();
      if(TickDOWN()==(price-point))
         OpenSELL();
   }
   //-- Если наступило время закрытия позиции, закрываем все
   if(IsTimeForClose())
      CloseALL();

//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+

//-- Выбираем позиции по текущему символу. Если позиции нет и выбирать нечего, то выходим!
   if(!PositionSelect(Symbol()))
      return;
//-- Стоп-лосс длинной позиции переставляем в безубыток и тралим его
   if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
     {
      SetBreakevenForBuyPosition(); // установить безубыток для Buy позиции
      TrailingStopLossForBuyPosition(); // перетащить Stop Loss для Buy позиции
     }
//-- Стоп-лосс короткой позиции переставляем в безубыток и тралим его
   else
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
        {
         SetBreakevenForSellPosition(); // установить безубыток для Sell позиции
         TrailingStopLossForSellPosition(); // перетащить Stop Loss для Sell позиции
        }
  }
  
//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| разрешенного времени для открытия позиции. В противном случае    |
//| возвращает ложь.                                                 |
//+------------------------------------------------------------------+  
bool IsTimeForOpen()
  {
   MqlDateTime time_current,time_open,time_open1;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   if(time_current.hour == time_open.hour &&
      time_current.min >= time_open.min &&
      time_current.min < time_open1.min
      )
      return true;
   else
      return false;
   }
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}
我还是不明白这些神奇的工作。在净空方面,它没有任何意义。在任何情况下,你都可以很容易地删除这个检查,因为它只在一个函数中完成。
附加的文件:
MrBrooklin.mq5  38 kb
 

OnInit块也是想多了,还是写得不太正确。首先,你应该尽量写标识符,而不是数字。返回INIT_SUCCEEDED,而不是-1。第二,这里的开关是过度的。这段代码应该包含if或switch。首先你必须写一个,然后写另一个--只是油。

第三,我们需要监控所有的账户类型。我们有Demo,然后我们有Real。然后是竞赛。但是,即使没有第三个账户,也应该有一个存根,可以抓住所有其他的变体

int OnInit()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   ACCOUNT_TRADE_MODE_CONTEST
   if(account_type==ACCOUNT_TRADE_MODE_REAL) //если торговый режим счёта - реальный
     {
      //--- выводим окно сообщений на торговом терминале и закрываем советник
      MessageBox("Работа на реальном счете запрещена, выходим!","Советник запущен на реальном счете");
      return(INIT_FAILED); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if(account_type==ACCOUNT_TRADE_MODE_DEMO) //если торговый режим счёта - демо
     {
      //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
      MessageBox("Работа на демо-счете разрешена!","Советник запущен на демо-счете");
      trade_mode="Счёт REAL";
      return(INIT_SUCCEEDED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
//-- Заглушка на случай непредвиденных вариантов. Всегда должна быть даже если варианта явно два.
   else
   {
      MessageBox("Неизвестный тип счета. Работа невозможна!");
      return(INIT_FAILED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
   }
  }
 

而锦上添花的是:评论。当我们写函数时,有很多空间,我们的手被吸引来把注释放在正确的代码块中。

//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}

当同样的代码被 "同时 "写在某个主函数的主体中时,它就会变得尽可能的短,而且没有注释。

if(PositionSelect(Symbol())==false
      && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
      && time_current.hour==time_open.hour
      && time_current.min>=time_open.min
      && time_current.min<time_open1.min
      && (TickUP()==(price+point)))
     {OpenBUY();}

所以多写一些函数,它们鼓励你写正确的注释,虽然它们使代码更加冗长,但也使它更加清晰。

第三点:你在这里写作。

//+------------------------------------------------------------------+
/ | Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit()
  {
  ...
  }

为什么要增加另一种类型的评论?只需用你的评论来取代块中的内容。

//+------------------------------------------------------------------------------------------------------+
//| Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции    |
//| return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то   |
//| это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше      |
//| можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.  |
//| "удачная инициализация".                                                                             |
//+------------------------------------------------------------------------------------------------------+
int OnInit()
  {
  ...
  }

记住,评论是给你的,而不是你给评论的。删除旧的,并添加你自己的。坚持格式--在函数头中,简要但清楚地在注释中说明该函数的作用,以及在什么情况下返回什么值。

 

顺便说一下,当职位关闭 时间条件被分离到一个单独的函数中时,很明显,它写得并不正确。

//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}

我从你的代码中提取了内部内容。很明显,只有在一分钟内才会平仓。В 23:50.这段代码会起作用,但如果在23:50时出了问题,那么在23:51时位置就会一直悬空。这就是为什么你至少要写出来。

time_current.min>=time_close.min)

而且,即使这种选择也不理想。一个更强大的解决方案是使用交易模式。然而,这是更高的水平。到目前为止,这种设计会做得很好。

 
MrBrooklin:

你好,瓦西里!非常感谢你的及时建议和支持。您对函数的作用和程序代码构造原则的评论,对我学习MQL5编程语言真的很有帮助。

  1. https://www.mql5.com/ru/forum/352460/page28#comment_18636493
  2. https://www.mql5.com/ru/forum/352460/page28#comment_18637800
  3. https://www.mql5.com/ru/forum/352460/page29#comment_18641729
  4. https://www.mql5.com/ru/forum/352460/page52#comment_18694985

现在,我脑子里的信息已经结构化了,不仅是我写的代码,其他程序员写的代码也更容易理解。我希望这个主题对那些开始从头开始学习MQL5编程语言的人来说是一个很好的帮助。

问候,弗拉基米尔。

干得好,弗拉基米尔。好的编码员通常有好的算法,他们有好的目标设定者......。一切都始于正确的目标,和正确的目的。你可以一下子建好房子,然后找水。你可以先找到水,在建造房屋时考虑到水。目标/目的,以及目标设定是由机会....你直接到算法....但总的来说,它是非常好的!

 
Vasiliy Sokolov:

顺便说一下,当职位关闭 时间条件被分离到一个单独的函数中时,很明显,它写得并不正确。

我从你的代码中提取了内部内容。很明显,只有在一分钟内才会平仓。В 23:50.这段代码会起作用,但如果在23:50时出了问题,那么在23:51时位置就会一直悬空。这就是为什么我们至少需要写出来。

而且,即使这种选择也不理想。一个更强大的解决方案是使用交易模式。然而,这是更高的水平。只要这个设计能够处理它。

瓦西里,你是一个伟大的老师!

没有任何入门读物或教科书能提供这样的解释。你建议的一切,我一定会在EA的最终版本中实施。

问候,弗拉基米尔。

 

我暂时偏离主题,给大家讲一个关于老师的人生故事。在我们的学院,当我们已经是专业的时候,我们有一个很好的老师。当时,我们正在研究逻辑的代数。许多学生在很长一段时间内无法理解1+1 如何能等于1?如果是1x1,显然就等于1。这就是你所拥有的!!!。这位老师用简单的例子给我们解释了什么是逻辑上的OR,什么是逻辑上的AND,这让我终生难忘。

老师说,想象一下,你需要在早上到达学院上课。你可以乘坐无轨电车 有轨电车前往该学院。你已经来到了公交车站,但既没有无轨电车(条件为假 或与0 相同),也没有有轨电车(条件为假 或与0 相同)。自然,你将无法进入该研究所(条件 false 或与0 相同)。检查0+0=0。妙不可言!如果你到达公共汽车站时,有一辆无轨电车(条件为真 或与1 相同), 一辆有轨电车(条件为真 或与1 相同), 同时有一辆无轨电车和一辆有轨电车,那么你肯定会到达研究所,条件为真 或与1 相同将成立。检查:1+0=10+1=11+1=1。一切都很合适!

他用无轨电车和有轨电车的同样例子,向我们解释了什么是逻辑

这就是教师天赋的力量!这就是教师天赋的力量。我将在我的余生中记住它!

真诚的,弗拉基米尔。

 
Valeriy Yastremskiy:

干得好。好的编码员通常有好的算法师,他们有目标设定者,他们有目标设定者......这一切都始于正确的目标,和正确的目的。你可以一下子建好房子,然后找水。你可以先找到水,在建造房屋时考虑到水。目标/目的,以及目标设定是由机会....你直接到算法....但总的来说,它是非常好的!

谢谢你,瓦莱里,感谢你对这个话题的参与和建设性的对话。

问候,弗拉基米尔。