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

 
MrBrooklin:

伊戈尔,学习MQL5语言的愿望并没有丧失,相反,它只会越来越强烈。我只是很纠结,为什么我这么笨,不能理解这个循环运算符的简单事情。最主要的是,如果没有循环运算符,代码可以完美地工作,但有了循环,就完全是一团糟。无 论如何,我都会把这件事弄清楚的。然而,自从隔离期结束后,我自学的时间少了很多,我在工作中也有难得的时间来学习语言。大多数情况下,我在晚上和深夜学习语言。

我需要解决2个任务,才能最终写出追踪止损的代码。

  1. 我需要写一段代码,使用for循环操作 来搜索所有符号的所有未结头寸,如果没有,则在09:00:00到09:01:00之间开一个买入头寸,并在23:50:00强行平仓,如果在交易日内没有触发止损。没有for循环操作符,就像我之前写的那样,一切工作都很完美。现在我想实现同样的结果,但要使用循环。
  2. 编写2个函数,通过从09:00:00到09:01:00的时间框架中出现的第一个刻度来确定开仓方向。如果第一个刻度线是向上的,就应该打开买入头寸;如果第一个刻度线是向下的,就应该相应地打开卖出头寸。这不是一个策略,这只是我不使用随机 条件的"愿望"
真诚的,弗拉基米尔。

弗拉基米尔,你正在进入某种奇怪的东西。有人给了你一个模板:https://www.mql5.com/ru/forum/352460/page58#comment_18726492 你没有理会,决定用你的智慧去做。这种方式很困难,可能需要几年时间。在路上,学习C、C++、Rust、C#、多态性、继承、指针的取消引用、动态类型转换。检查或驾驶。你必须走最短的路来达到目的。如果你想要尾随,就写尾随。你不需要学习C和C++就能做到这一点。如果你愿意的话,这都可以作为选修课。

for 循环的情况就很说明问题。这是一个强大的建筑,必须应用在正确的地方。你有网状物和一个辅助算法。这就是为什么没有列举职位的原因,没有法师,所以你没有这种循环。当然,没有它一切都能正常工作,但这个循环只会让你感觉像油一样。但你固执地试图使用它,它似乎本身就是一个目的:尽可能多地使用,尽可能多地拥有。

 
Vasiliy Sokolov:

弗拉基米尔,你有点陷入困境了。有人给了你一个模板:https://www.mql5.com/ru/forum/352460/page58#comment_18726492,你没有理会,决定按自己的想法行事。这条路很困难,可能需要几年时间。在你的路上,学习C、C++、Rust、C#、多态性、继承、指针的取消引用、动态类型转换。检查或驾驶。你必须采取最短的方式来实现目标。如果你想要尾随,就写尾随。你不需要学习C和C++就能做到这一点。如果你愿意,这一切都可以作为选修课。

for 循环的情况就很说明问题。这是一个强大的建筑,必须应用在正确的地方。你有网状物和一个辅助算法。这就是为什么没有列举职位的原因,没有法师,所以你没有这种循环。自然,没有它一切都能正常工作,但这个循环只生产石油。但你固执地想使用它,所以它似乎本身就是一个目的:你可以尽可能多地使用所有的东西和任何东西来保持库存。

你好,瓦西里!

感谢你及时加入这个话题,并一如既往地指导我走正确的道路。现在让我按一个顺序来解释一切。

  1. 专家顾问中的一个模板正是在您好心提供给我的时候插入的,对此我想表示感谢!
  2. 我决定在策略测试器中 测试一个带有Trailin Stop的EA,但我不知道如何在不自动打开至少一个头寸的情况下进行测试。这就是为什么我在编写 "成熟的 "专家顾问时开始感到焦虑。我把它放在引号里是因为我的专家顾问没有这样的策略。我根本不想用随机 条件来开仓。
  3. 除了你之外,专家顾问的对话者都没有提出在这种情况下用for 循环来搜索位置是没有必要的。我的错,我自己早就可以猜到,并问--真的有必要循环吗?结果是,你不知道。再次感谢您的提示!
  4. 为了最终去写追踪止损,而不是半途而废,我只需要写2个函数的代码,确定净买入或卖出头寸的开仓方向。
  5. 是的,我完全同意你的观点,自学开始拖后腿了。但是,如果考虑到我走的是一条未经探索的歪路,我希望有可能打个折扣。
  6. 在整个自学期间,我得到了大量的信息,现在我不仅要努力安排这些信息,还要在实践中使用这些信息。为此,我从CodeBase下载了一个专家顾问,开发了它并在一个外汇交易商的模拟账户上使用了两个星期。结果与我的预期完全一致。很快我将开始在一个真实的账户上使用它。这已经是一个加分项了!
  7. 我不追求自学的速度,因为我在前面已经写过,过量的信息不时会出现昏昏沉沉的情况。
  8. 在整个自学期间,一点一点地开始背诵汇编代码后出现的英语单词和短语。我现在较少在谷歌翻译中寻找。这也是自我学习的一个积极时刻。
  9. 当一个完整的跟踪止损的工件准备好后,我将准确地发布我在专家顾问中写的所有内容。我将努力在星期六完成它。编写尾随止损代码的主要工作将从这里开始。

瓦西里,我非常感谢你的指导和建设性的批评!我还要感谢这个主题的所有参与者,他们帮助我从头开始自学MQL5编程语言

自学继续进行。待续。

问候,弗拉基米尔。

 
Андрей:

大家下午好!

如果我想补充一些有趣的东西,我认为 "从头开始自我培训MQL5"的想法不太正确。 如果一个人在编程方面是0,他必须首先学习如何编程。如果目的是要从头开始学习如何用MQL编程,一个人必须首先学习如何用C语言编程,至少是最基本的,然后再学习用MQL写作,因为MQL实际上是C语言,但它是专门用于某种任务的,一个人不了解循环的工作原理,已经在尝试写程序。这就像在不懂俄语的情况下试图学习拉丁语......

另一点是,C语言对学习编程并不友好,它很简单,但过于简洁,如果没有对算法、数据类型、布尔代数的基本了解,初学者很难阅读和理解代码。 在C语言中,一排三个字符可能意味着其他语言中的几个字符串。

要想简单地从头开始学习编程,对于基础,学习用一种(不管是什么)语言与计算机对话,我建议从Pascal开始,这是最简单的书,可以从它开始(2-3周的时间就可以了),然后是C(在Pascal之后最大的一周勤奋,掌握语法,我是说基本语言技能!)。),然后拿起专门的语言,如MQL,因为MQL是C,它有一点扭曲和简化,因为它是为单一任务设计的,而且编译器知道明显的。而C又是一种RAR压缩的Pascal,但Pascal几乎是英语=)。

现在说说循环。

当我学习编程时,与代数中的和的类比帮助了我,你指定了n的初始值、n的最终值以及用这个n来计算的公式。

你好,安德烈!我完全同意你对基础的看法。我没有,也从来没有过。但正如俗话所说--莫斯科不是一下子就能建成的!谢谢你的建议和提示

问候,弗拉基米尔。

 
Roman Shiredchenko:

是的,我同意--他的代码也很好!你是在无事生非的情况下把自己搞得焦头烂额。慢慢整理代码,不要急于求成,就这样。这里有一个基本的东西-- 选择和拖曳:(多么简单--只是你选择NEED位置的话题...:-))

除此之外,包含文件--这意味着它们在你的代码中也是作为函数存在的,就这样了。

谢谢你,罗曼,谢谢你的代码和链接!我正在冷静地处理这些代码,肯定不会在任何地方匆忙行事!"。

真诚的,弗拉基米尔。

 

美好的一天,祝大家心情愉快!

我继续学习MQL5 编程语言。今天,正如承诺的那样,我发布给大家看的 EA模板的代码是为编写追踪止损而设计的。由于 EA 模板代码非常繁琐,所以我们不得不尽可能减少注释。 EA 交易的完整版本以及每行代码的详细注释,在一个可供编程学校一年级学生访问的演示文稿中,稍后将以工作名称Trailing_Stop.mq5的文件形式发布,因此以免在现场产生另一个“脚布”。

在发布代码之前,EA 在策略测试器中进行了测试。没有发现任何问题。 EA 使用净额头寸会计系统。这种会计系统意味着同一时间在同一交易品种(金融工具)的账户上只能有一个未平仓头寸

目前,EA 有能力:

  1. 检查计划安装它的交易账户。如果顾问安装在模拟账户上,则继续工作的权限将出现在消息窗口中。如果尝试在真实账户上安装顾问,则消息窗口中将出现有关无法继续工作的警告,之后它将自动从交易终端的工作窗口中删除。
  2. 每个交易日上午 9:00 准时进行一次。根据莫斯科时间,在第一个刻度指向的方向上自动开仓。例如,如果在上午 9:00莫斯科时间,第一个刻度将向上,这意味着将打开一个买入仓位,如果第一个刻度向下,则将打开一个卖出仓位。
  3. 设置止损大小。
  4. 设置手数。
  5. 设置追踪止损起作用所需的其他参数。

此外,EA 预装了一个模板,由 Vasily Sokolov 友情提供,在自学过程中会填充代码。

一个警告!!!

EA 是根据模拟账户的自学计划开发的,仅用于教育目的!不适用于在真实账户上进行交易并获利!

最好的问候,弗拉基米尔。

 //+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright    "Copyright 2020, MetaQuotes Software Corp."
#property link          "https://www.mql5.com"
#property description "Советник создан для демо-счёта исключительно в учебных целях!"
#property description "Не предназначен для торговли на реальном счёте и извлечения прибыли!"
#property version      "1.00"
/* Краткое описание советника Trailing_Stop. Код советника разрабатывается в соответствии с
   планом самообучения языку программирования MQL5. В советнике применена неттинговая система
   учёта позиций. Эта система учета подразумевает, что в один момент времени на счете может быть
   только одна открытая позиция по одному и тому же символу (финансовому инструменту). Алгоритм
   работы советника прост: один раз в торговый день ровно в 9 ч. 00 мин. по московскому времени
   будет открываться одна позиция в ту сторону, куда будет направлен первый тик. Например, если
   первый тик будет направлен вверх, то значит откроется позиция BUY, если первый тик будет вниз,
   то значит откроется позиция SELL. У открытой позиции сразу будет устанавливаться ордер Stop Loss
   фиксированного размера для минимизации потерь в том случае, если цена финансового инструмента
   станет двигаться в убыточном направлении. Если цена инструмента достигнет этого уровня, то
   позиция полностью закроется автоматически. Если цена финансового инструмента будет двигаться
   в прибыльном направлении, то тогда автоматически включится в работу Trailing_Stop (Трейлинг Стоп).
   Схема работы Трейлинг Стоп:
   1. С приходом новых котировок советник проверяет, прибыльна ли открытая позиция.
   2. Как только прибыль (в пунктах) станет равной либо большей той величины, которая указана во
      входном параметре советника "Уровень перестановки Stop Loss в безубыток", автоматически
      поступит команда для перемещения ордера Stop Loss на тот уровень цены, по которому открылась
      существующая позиция, т.е. в безубыток.
   3. Если цена и дальше продолжит движение с увеличением прибыльности позиции, то при превышении
      величины, указаной во входном параметре советника "Уровень перестановки Stop Loss в безубыток"
      на величину, которая указана во входном параметре "Шаг трейлинг стопа", Stop Loss вслед за
      текущей ценой автоматически переместится с уровня безубытка на величину этого шага.
   4. Если прибыльность позиции уменьшится, то модификации ордера происходить не будет. Таким
      образом, будет автоматически фиксироваться прибыль торговой позиции.
   Если в течении торгового дня открытая позиция не закроется по Stop Loss или Trailing_Stop, то в
   23 ч. 50 мин. советник принудительно закроет эту позицию.

   ВАЖНО!!! Советник создан для демо-счёта исключительно в учебных целях!
            Не предназначен для торговли на реальном счёте и извлечения прибыли!*/

//--- Создадим входные параметры советника
input ushort BreakevenLevel= 100 ; //Уровень перестановки Stop Loss в безубыток
input ushort TrailingStop= 10 ;     //Шаг трейлинг стопа
input ushort SL= 200 ;             //Стоп-лосс
input double Lot= 0.1 ;             //Лот
input long    Magic_Number= 3215 ;   //Магический номер
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit ()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=( ENUM_ACCOUNT_TRADE_MODE ) AccountInfoInteger ( ACCOUNT_TRADE_MODE );
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   /* Создадим оператор-переключатель switch, который cравнивает значение выражения с константами во всех
      вариантах case и передает управление оператору с соответствующим значением выражения.*/
   switch (account_type)
     {
       case ACCOUNT_TRADE_MODE_DEMO : //если ключевое слово ACCOUNT_TRADE_MODE_DEMO
         trade_mode= "Счёт DEMO" ;     //значит торговый режим счёта - демо
         break ;                     //оператор break прекращает выполнение ближайшего оператора switch
       case ACCOUNT_TRADE_MODE_REAL : //если ключевое слово ACCOUNT_TRADE_MODE_REAL
         trade_mode= "Счёт REAL" ;     //значит торговый режим счёта - реальный
         break ;                     //оператор break прекращает выполнение ближайшего оператора switch
     }
   if (account_type== ACCOUNT_TRADE_MODE_REAL ) //если торговый режим счёта - реальный
     {
       //--- выводим окно сообщений на торговом терминале и закрываем советник
       MessageBox ( "Работа на реальном счете запрещена, выходим!" , "Советник запущен на реальном счете" );
       return (- 1 ); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if (account_type== ACCOUNT_TRADE_MODE_DEMO ) //если торговый режим счёта - демо
     {
       //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
       MessageBox ( "Работа на демо-счете разрешена!" , "Советник запущен на демо-счете" );
       return ( 0 ); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
   return ( INIT_SUCCEEDED );
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit ( const int reason)
  {
//--- сообщим код завершения работы эксперта
   Print ( __FILE__ , ": Код причины деинициализации = " ,reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick ()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct ( TimeCurrent (),time_current);
   TimeToStruct (( D'1970.01.01 09:00:00' ),time_open);
   TimeToStruct (( D'1970.01.01 09:01:00' ),time_open1);
   TimeToStruct (( D'1970.01.01 23:50:00' ),time_close);
//--- Зададим условия для открытия позиций 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);
   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();}
   else
     {
       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
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if (time_current.hour==time_close.hour && time_current.min==time_close.min)
      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 позиции
        }
  }

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer ()
  {
//---

  }
//+------------------------------------------------------------------+
//| Buy Position Open function                                       |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Buy и назовём её OpenBUY
void OpenBUY()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= { 0 };
   MqlTradeResult result= { 0 };
//--- Создадим переменные необходимые для работы с сервером
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   request.action= TRADE_ACTION_DEAL ;
   request.symbol= Symbol ();
   request.volume=Lot;
   request.deviation= 2 ;
   request.magic=Magic_Number;
   request.type= ORDER_TYPE_BUY ;
   request.price= NormalizeDouble (price,digits);
   /*Создадим запрос на торговый сервер request.sl, в котором укажем, где должен находиться уровень
     стоп лосс относительно цены, по которой открылась позиция "BUY". Уровень SL должен находиться
     ниже цены, поэтому пишем price-SL*point. Для нормализации уровня SL применим функцию преобразования
     данных NormalizeDouble, где обязательно умножим уровень SL на point (размер одного пункта) и укажем
     digits(количество знаков после запятой).
   */
   request.sl= NormalizeDouble (price-SL*point,digits);
   if (! OrderSend (request,result))
       PrintFormat ( "OrderSend error %d" , GetLastError ());
   PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| Sell Position Open function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Sell и назовём её OpenSELL
void OpenSELL()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= { 0 };
   MqlTradeResult result= { 0 };
//--- Создадим переменные необходимые для работы с сервером
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   request.action= TRADE_ACTION_DEAL ;
   request.symbol= Symbol ();
   request.volume=Lot;
   request.deviation= 2 ;
   request.magic=Magic_Number;
   request.type= ORDER_TYPE_SELL ;
   request.price= NormalizeDouble (price,digits);
   /*Точно также создадим запрос на торговый сервер request.sl, в котором укажем, где должен
     находиться уровень стоп лосс относительно цены, по которой открылась позиция "SELL".
     Уровень SL теперь должен находиться выше цены, поэтому пишем price+SL*point. Снова для
     нормализации уровня SL применим функцию преобразования данных NormalizeDouble, где обязательно
     умножим уровень SL на point (размер одного пункта) и укажем digits(количество знаков после запятой).
   */
   request.sl= NormalizeDouble (price+SL*point,digits);
   if (! OrderSend (request,result))
       PrintFormat ( "OrderSend error %d" , GetLastError ());
   PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| All Position Close function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для закрытия всех позиций и назовем её CloseALL
void CloseALL()
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request;
   MqlTradeResult   result;
//--- перебираем все открытые позиции
   for ( int i= PositionsTotal ()- 1 ; i>= 0 ; i--)
     {
       //--- определяем параметры
       ulong   ticket= PositionGetTicket (i);                                             // тикет позиции
       string symbol= PositionGetString ( POSITION_SYMBOL );                               // символ
       int     digits=( int ) SymbolInfoInteger (symbol, SYMBOL_DIGITS );                     // количество знаков после запятой
       ulong   magic= PositionGetInteger ( POSITION_MAGIC );                               // MagicNumber позиции
       double volume= PositionGetDouble ( POSITION_VOLUME );                               // объем позиции
       ENUM_POSITION_TYPE type=( ENUM_POSITION_TYPE ) PositionGetInteger ( POSITION_TYPE ); // тип позиции
       //--- выводим информацию о позиции
       PrintFormat ( "#%I64u %s  %s  %.2f  %s [%I64d]" ,
                  ticket,
                  symbol,
                   EnumToString (type),
                  volume,
                   DoubleToString ( PositionGetDouble ( POSITION_PRICE_OPEN ),digits),
                  magic);
       //--- если MagicNumber совпадает
       if (magic==Magic_Number)
        {
         //--- обнуляем значения запроса на сервер и результата ответа
         ZeroMemory (request);
         ZeroMemory (result);
         //--- устанавливаем параметры операции
         request.action   = TRADE_ACTION_DEAL ;   // тип торговой операции (немедленное исполнение)
         request.position =ticket;               // тикет позиции
         request.symbol   =symbol;               // символ
         request.volume   =volume;               // объём позиции
         request.deviation= 2 ;                   // допустимое отклонение от цены
         request.magic    =Magic_Number;         // MagicNumber позиции
         if (type== POSITION_TYPE_BUY )             // если тип позиции "покупка"
           {
             //--- запрашиваем лучшее предложение цены на "продажу" по текущему символу
            request.price= SymbolInfoDouble (symbol, SYMBOL_BID );
             //--- запрашиваем тип рыночного ордера на "продажу" и закрываем позицию встречным ордером
            request.type = ORDER_TYPE_SELL ;
           }
         else // в противном случае
           {
             //--- запрашиваем лучшее предложение цены на "покупку" по текущему символу
            request.price= SymbolInfoDouble (symbol, SYMBOL_ASK );
             //--- запрашиваем тип рыночного ордера на "покупку" и закрываем позицию встречным ордером
            request.type = ORDER_TYPE_BUY ;
           }
         //--- выводим информацию о закрытии
         PrintFormat ( "Close #%I64d %s %s" ,ticket,symbol, EnumToString (type));
         //--- отправляем запрос
         if (! OrderSend (request,result))
             PrintFormat ( "OrderSend error %d" , GetLastError ());   // если отправить запрос не удалось, выводим код ошибки
         //--- информация об операции
         PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
         //---
        }
     }
  }
//+------------------------------------------------------------------+
//| Tick Up function                                                 |
//+------------------------------------------------------------------+
double TickUP()
  {
//---
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   double price1= NormalizeDouble (price+point,digits);
   return (price1);
  }
//+------------------------------------------------------------------+
//| Tick DOWN function                                               |
//+------------------------------------------------------------------+
double TickDOWN()
  {
//---
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   double price2= NormalizeDouble (price-point,digits);
   return (price2);
  }
  
//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+
  
//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // установить безубыток для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции BUY вслед за ценой                      |
//+------------------------------------------------------------------+
void TrailingStopLossForBuyPosition() // перетащить Stop Loss для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // установить безубыток для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции SELL вслед за ценой                     |
//+------------------------------------------------------------------+
void TrailingStopLossForSellPosition() // перетащить Stop Loss для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
 

大家今天好,心情好

我继续学习MQL5编程语言。昨天我写了卖出和买入头寸的收支平衡止损 的代码。我粘贴的代码还没有任何评论。我将在稍后发布专家顾问的完整版本,并对每行代码进行详细说明,其形式可供编程学校一年级学生使用,文件名为Trailing_Stop.mq5。在发布代码之前,我们在策略测试器中 检查了专家顾问的性能。没有发现问题。

问候,弗拉基米尔。

//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // функция "Установить безубыток для позиции Buy"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Buy
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Buy
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open+BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Buy
         if(type==POSITION_TYPE_BUY && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // функция "Установить безубыток для позиции Sell"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Sell
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Sell
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open-BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Sell
         if(type==POSITION_TYPE_SELL && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
 
MrBrooklin:

大家今天好,心情好

我继续学习MQL5编程语言。今天,正如承诺的那样,我粘贴了我的专家顾问模板 的代码,该模板旨在编写一个跟踪止损。由于模板EA的代码相当繁琐,我不得不尽可能缩短注释。专家顾问的完整版本,包括对每一行代码的详细评论,以一年级编程学生可以理解的形式,稍后将作为一个文件发布,工作名称为Trailing_Stop.mq5,以避免在网站上造成另一种破坏。

...

非常好。主旨把握得很好。特别是我喜欢这个发展过程。

MrBrooklin:

大家今天好,心情好

我继续学习MQL5编程语言。昨天我写了买入和卖出头寸的盈亏平衡止损 的代码。

也就是说,一切都很正确。你不必在一个地方一次解决所有问题。你应该逐渐解决它,就像你做的那样。首先,你描述了OnTick函数中的基本逻辑以及OpenBUY、OpenSELL、TickUP、TickDown等基本函数。

然后,当这个基本代码被调试、编译并在其能力范围内工作时,你开始描述其他功能。这可能在第二天或甚至一周内完成。这意味着你不需要改变主代码。我想你可以看到原因。

现在你需要磨练这种开发方式:继续编写函数,通过它们将功能与现有的代码联系起来。现有的程序应该可以正常工作。复杂性不应增加。要添加的函数必须返回简单、清晰的结果。他们的名字对你来说应该是一样的简单和清晰。如果你做不到这一点,可能是因为你需要写的不是一个而是两个甚至三个函数来解决这个问题。起初,应用程序的整体框架将很难界定。这就是为什么你最好向更有经验的同事征求意见。随着时间的推移,你将学会自己开发这种模板。

随着时间的推移,你将需要在 函数中使用越来越多的语言结构,使其符合你的需要。在这一点上,你将逐渐地,最重要的是,有机地扩展你对语言结构的知识,学习循环、数组,甚至是与指针打交道。

事实上,你已经学会了编程中最难的部分,尽管你还没有巩固你所学的材料。你现在需要做的就是多次重复以巩固它。从这里开始,不会有什么根本性的新东西,一切都一样:一般模板->描述函数的名称及其参数->编写这些函数的内容->在中央单元中安排这些函数。就这样了。剩下的就是完善这项技能并保持一致。你开始使用的各种附加结构,如循环和数组,只是细节,有时做得很巧妙,但也不过是细节而已。

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

既然完美无缺,我就在代码上多加几句评论。

void OnTick()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   TimeToStruct((D'1970.01.01 23:50:00'),time_close);
//--- Зададим условия для открытия позиций 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);
   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();}
   else
     {
      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
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if(time_current.hour==time_close.hour && time_current.min==time_close.min)
      CloseALL();
}

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

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

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

TickUP()==(price+point)

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

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

 
Vasiliy Sokolov:

非常好。主要观点抓得很准。特别是喜欢这个发展过程。

也就是说,一切都很正确。你不必在一个地方一下子解决这个问题。你要逐渐地做,就像你所做的那样。首先,你描述了OnTick函数中的基本逻辑以及OpenBUY、OpenSELL、TickUP、TickDown等基本函数。

然后,当这个基本代码被调试、编译并在其能力范围内工作时,你开始描述其他功能。这可能在第二天或甚至一周内完成。这意味着你不需要改变主代码。我想你可以看到原因。

现在你需要磨练这种开发方式:继续编写函数,通过它们将功能与现有的代码联系起来。现有的程序应该可以正常工作。复杂性不应增加。要添加的函数必须返回简单、清晰的结果。他们的名字对你来说应该是一样的简单和清晰。如果你做不到这一点,可能你需要写的不是一个而是两个甚至三个函数来解决这个问题。

为了使一个函数能够完成其指定的任务,你最终将需要在这种函数中使用越来越多的语言结构。这时,你会逐渐地,最重要的是,有机地扩展你的语言结构知识,学习循环、数组,甚至与指针打交道。

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

问候,弗拉基米尔。